Ensure that Utilities(Backup/Restore/Maintenence/Import-Export) should not be started
if binary path is wrong and also added 'Stop Process' button to cancel the process.
@ -113,6 +113,8 @@ When you’ve specified the details that will be incorporated into the pg_dump c
|
|||||||
.. image:: images/backup_messages.png
|
.. image:: images/backup_messages.png
|
||||||
:alt: Backup success notification popup
|
:alt: Backup success notification popup
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Backup process.
|
||||||
|
|
||||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||||
|
|
||||||
.. image:: images/backup_process_watcher.png
|
.. image:: images/backup_process_watcher.png
|
||||||
|
@ -24,6 +24,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
|||||||
.. image:: images/backup_globals_messages.png
|
.. image:: images/backup_globals_messages.png
|
||||||
:alt: Backup globals success notification popup
|
:alt: Backup globals success notification popup
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Backup process.
|
||||||
|
|
||||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||||
|
|
||||||
.. image:: images/backup_globals_process_watcher.png
|
.. image:: images/backup_globals_process_watcher.png
|
||||||
|
@ -78,6 +78,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
|||||||
.. image:: images/backup_server_messages.png
|
.. image:: images/backup_server_messages.png
|
||||||
:alt: Backup server success notification popup
|
:alt: Backup server success notification popup
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Backup process.
|
||||||
|
|
||||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||||
|
|
||||||
.. image:: images/backup_server_process_watcher.png
|
.. image:: images/backup_server_process_watcher.png
|
||||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 340 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 204 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 322 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 149 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 228 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 259 KiB |
@ -53,6 +53,8 @@ After completing the *Import/Export data* dialog, click the *OK* button to perfo
|
|||||||
.. image:: images/import_export_complete.png
|
.. image:: images/import_export_complete.png
|
||||||
:alt: Import Export data completion notification
|
:alt: Import Export data completion notification
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Import/Export process.
|
||||||
|
|
||||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||||
|
|
||||||
.. image:: images/import_export_pw.png
|
.. image:: images/import_export_pw.png
|
||||||
|
@ -34,6 +34,8 @@ pgAdmin will inform you when the background process completes:
|
|||||||
.. image:: images/maintenance_complete.png
|
.. image:: images/maintenance_complete.png
|
||||||
:alt: Maintenance completion notification
|
:alt: Maintenance completion notification
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Maintenance process.
|
||||||
|
|
||||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||||
|
|
||||||
.. image:: images/maintenance_pw.png
|
.. image:: images/maintenance_pw.png
|
||||||
|
@ -17,6 +17,7 @@ Features
|
|||||||
Bug fixes
|
Bug fixes
|
||||||
*********
|
*********
|
||||||
|
|
||||||
|
| `Bug #3232 <https://redmine.postgresql.org/issues/3232>`_ - Ensure that Utilities(Backup/Restore/Maintenence/Import-Export) should not be started if binary path is wrong and also added 'Stop Process' button to cancel the process.
|
||||||
| `Bug #3638 <https://redmine.postgresql.org/issues/3638>`_ - Fix syntax error when creating new pgAgent schedules with a start date/time and exception.
|
| `Bug #3638 <https://redmine.postgresql.org/issues/3638>`_ - Fix syntax error when creating new pgAgent schedules with a start date/time and exception.
|
||||||
| `Bug #3674 <https://redmine.postgresql.org/issues/3674>`_ - Cleanup session files periodically.
|
| `Bug #3674 <https://redmine.postgresql.org/issues/3674>`_ - Cleanup session files periodically.
|
||||||
| `Bug #3660 <https://redmine.postgresql.org/issues/3660>`_ - Rename the 'SQL Editor' section of the Preferences to 'Query Tool' as it applies to the whole tool, not just the editor.
|
| `Bug #3660 <https://redmine.postgresql.org/issues/3660>`_ - Rename the 'SQL Editor' section of the Preferences to 'Query Tool' as it applies to the whole tool, not just the editor.
|
||||||
|
@ -84,6 +84,8 @@ When you’ve specified the details that will be incorporated into the pg_restor
|
|||||||
.. image:: images/restore_messages.png
|
.. image:: images/restore_messages.png
|
||||||
:alt: Restore dialog notifications
|
:alt: Restore dialog notifications
|
||||||
|
|
||||||
|
Use the **Stop Process** button to stop the Restore process.
|
||||||
|
|
||||||
Click *Click here for details* on the popup to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the restore, and provides additional information for troubleshooting should the restore command encounter problems.
|
Click *Click here for details* on the popup to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the restore, and provides additional information for troubleshooting should the restore command encounter problems.
|
||||||
|
|
||||||
.. image:: images/restore_process_watcher.png
|
.. image:: images/restore_process_watcher.png
|
||||||
|
@ -28,6 +28,7 @@ speaklater==1.3
|
|||||||
sqlparse==0.2.4
|
sqlparse==0.2.4
|
||||||
WTForms==2.1
|
WTForms==2.1
|
||||||
Flask-Paranoid==0.2.0
|
Flask-Paranoid==0.2.0
|
||||||
|
psutil==5.4.7
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Modules specifically requires for Python2.7 or greater version
|
# Modules specifically requires for Python2.7 or greater version
|
||||||
|
34
web/migrations/versions/ece2e76bf60e_.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
""" Added utility pid to stop process
|
||||||
|
|
||||||
|
Revision ID: ece2e76bf60e
|
||||||
|
Revises: ca00ec32581b
|
||||||
|
Create Date: 2018-10-18 14:45:13.483068
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pgadmin.model import db
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'ece2e76bf60e'
|
||||||
|
down_revision = 'ca00ec32581b'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
db.engine.execute(
|
||||||
|
'ALTER TABLE process ADD COLUMN utility_pid INTEGER'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
pass
|
@ -44,7 +44,8 @@ class BGProcessModule(PgAdminModule):
|
|||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
'bgprocess.status', 'bgprocess.detailed_status',
|
'bgprocess.status', 'bgprocess.detailed_status',
|
||||||
'bgprocess.acknowledge', 'bgprocess.list'
|
'bgprocess.acknowledge', 'bgprocess.list',
|
||||||
|
'bgprocess.stop_process'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -104,3 +105,18 @@ def acknowledge(pid):
|
|||||||
return success_return()
|
return success_return()
|
||||||
except LookupError as lerr:
|
except LookupError as lerr:
|
||||||
return gone(errormsg=str(lerr))
|
return gone(errormsg=str(lerr))
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route('/stop/<pid>', methods=['PUT'], endpoint='stop_process')
|
||||||
|
@login_required
|
||||||
|
def stop_process(pid):
|
||||||
|
"""
|
||||||
|
User has stopped the process
|
||||||
|
|
||||||
|
:param pid: Process ID
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
BatchProcess.stop_process(pid)
|
||||||
|
return success_return()
|
||||||
|
except LookupError as lerr:
|
||||||
|
return gone(errormsg=str(lerr))
|
||||||
|
@ -321,6 +321,14 @@ def execute():
|
|||||||
process = Popen(
|
process = Popen(
|
||||||
command, stdout=PIPE, stderr=PIPE, stdin=None, **kwargs
|
command, stdout=PIPE, stderr=PIPE, stdin=None, **kwargs
|
||||||
)
|
)
|
||||||
|
args.update({
|
||||||
|
'start_time': get_current_time(),
|
||||||
|
'stdout': process_stdout.log,
|
||||||
|
'stderr': process_stderr.log,
|
||||||
|
'pid': process.pid
|
||||||
|
})
|
||||||
|
update_status(**args)
|
||||||
|
_log('Status updated after starting child process...')
|
||||||
|
|
||||||
_log('Attaching the loggers to stdout, and stderr...')
|
_log('Attaching the loggers to stdout, and stderr...')
|
||||||
# Attach the stream to the process logger, and start logging.
|
# Attach the stream to the process logger, and start logging.
|
||||||
|
@ -14,6 +14,7 @@ Introduce a function to run the process executor in detached mode.
|
|||||||
import csv
|
import csv
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import psutil
|
||||||
from abc import ABCMeta, abstractproperty, abstractmethod
|
from abc import ABCMeta, abstractproperty, abstractmethod
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pickle import dumps, loads
|
from pickle import dumps, loads
|
||||||
@ -523,6 +524,10 @@ class BatchProcess(object):
|
|||||||
if 'end_time' in data and data['end_time']:
|
if 'end_time' in data and data['end_time']:
|
||||||
p.end_time = data['end_time']
|
p.end_time = data['end_time']
|
||||||
|
|
||||||
|
# get the pid of the utility.
|
||||||
|
if 'pid' in data:
|
||||||
|
p.utility_pid = data['pid']
|
||||||
|
|
||||||
return True, True
|
return True, True
|
||||||
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@ -657,3 +662,26 @@ class BatchProcess(object):
|
|||||||
|
|
||||||
if 'env' in kwargs:
|
if 'env' in kwargs:
|
||||||
self.env.update(kwargs['env'])
|
self.env.update(kwargs['env'])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stop_process(_pid):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
p = Process.query.filter_by(
|
||||||
|
user_id=current_user.id, pid=_pid
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if p is None:
|
||||||
|
raise LookupError(
|
||||||
|
_("Could not find a process with the specified ID.")
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = psutil.Process(p.utility_pid)
|
||||||
|
process.terminate()
|
||||||
|
except psutil.Error as e:
|
||||||
|
current_app.logger.warning(
|
||||||
|
_("Unable to kill the background process '{0}'").format(
|
||||||
|
p.utility_pid)
|
||||||
|
)
|
||||||
|
current_app.logger.exception(e)
|
||||||
|
@ -95,6 +95,10 @@ define('misc.bgprocess', [
|
|||||||
return url_for('bgprocess.acknowledge', {
|
return url_for('bgprocess.acknowledge', {
|
||||||
'pid': this.id,
|
'pid': this.id,
|
||||||
});
|
});
|
||||||
|
case 'stop_process':
|
||||||
|
return url_for('bgprocess.stop_process', {
|
||||||
|
'pid': this.id,
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return url_for('bgprocess.list');
|
return url_for('bgprocess.list');
|
||||||
}
|
}
|
||||||
@ -258,7 +262,22 @@ define('misc.bgprocess', [
|
|||||||
$('<div></div>', {
|
$('<div></div>', {
|
||||||
class: 'pg-bg-start',
|
class: 'pg-bg-start',
|
||||||
}).append(
|
}).append(
|
||||||
$('<div></div>').text(self.stime.toString())
|
$('<div></div>', {
|
||||||
|
class: 'row align-items-center',
|
||||||
|
}).append(
|
||||||
|
$('<div></div>', {
|
||||||
|
class: 'col-9',
|
||||||
|
}).text(self.stime.toString())
|
||||||
|
).append(
|
||||||
|
$('<div></div>', {
|
||||||
|
class: 'col-3',
|
||||||
|
}).append(
|
||||||
|
$('<button></button>', {
|
||||||
|
type: 'button',
|
||||||
|
class: 'btn btn-danger btn-sm float-right bg-process-stop',
|
||||||
|
}).text('Stop Process')
|
||||||
|
)
|
||||||
|
)
|
||||||
).append(
|
).append(
|
||||||
$('<div class="pg-bg-etime"></div>')
|
$('<div class="pg-bg-etime"></div>')
|
||||||
)
|
)
|
||||||
@ -313,6 +332,9 @@ define('misc.bgprocess', [
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// On Click event to stop the process.
|
||||||
|
content.find('.bg-process-stop').off('click').on('click', self.stop_process.bind(this));
|
||||||
}
|
}
|
||||||
// TODO:: Formatted execution time
|
// TODO:: Formatted execution time
|
||||||
self.container.find('.pg-bg-etime').empty().append(
|
self.container.find('.pg-bg-etime').empty().append(
|
||||||
@ -332,6 +354,14 @@ define('misc.bgprocess', [
|
|||||||
} else if (self.exit_code == 1) {
|
} else if (self.exit_code == 1) {
|
||||||
$status_bar.addClass('bg-failed');
|
$status_bar.addClass('bg-failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable/Disable stop process button
|
||||||
|
var $btn_stop_process = $(self.container.find('.bg-process-stop'));
|
||||||
|
if (isNaN(parseInt(self.exit_code))) {
|
||||||
|
$btn_stop_process.removeClass('disabled');
|
||||||
|
} else {
|
||||||
|
$btn_stop_process.addClass('disabled');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.show_detailed_view.apply(self);
|
self.show_detailed_view.apply(self);
|
||||||
}
|
}
|
||||||
@ -359,7 +389,18 @@ define('misc.bgprocess', [
|
|||||||
),
|
),
|
||||||
$logs = container.find('.bg-process-watcher'),
|
$logs = container.find('.bg-process-watcher'),
|
||||||
$header = container.find('.bg-process-details'),
|
$header = container.find('.bg-process-details'),
|
||||||
$footer = container.find('.bg-process-footer');
|
$footer = container.find('.bg-process-footer'),
|
||||||
|
$btn_stop_process = container.find('.bg-process-stop');
|
||||||
|
|
||||||
|
// Enable/Disable stop process button
|
||||||
|
if (isNaN(parseInt(self.exit_code))) {
|
||||||
|
$btn_stop_process.removeClass('disabled');
|
||||||
|
} else {
|
||||||
|
$btn_stop_process.addClass('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Click event to stop the process.
|
||||||
|
$btn_stop_process.off('click').on('click', self.stop_process.bind(this));
|
||||||
|
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
// set logs
|
// set logs
|
||||||
@ -439,6 +480,25 @@ define('misc.bgprocess', [
|
|||||||
console.warn(arguments);
|
console.warn(arguments);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
stop_process: function() {
|
||||||
|
var self = this;
|
||||||
|
$.ajax({
|
||||||
|
type: 'PUT',
|
||||||
|
timeout: 30000,
|
||||||
|
url: self.bgprocess_url('stop_process'),
|
||||||
|
cache: false,
|
||||||
|
async: true,
|
||||||
|
contentType: 'application/json',
|
||||||
|
})
|
||||||
|
.done(function() {
|
||||||
|
return;
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
console.warn(arguments);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_.extend(
|
_.extend(
|
||||||
@ -538,10 +598,18 @@ define('misc.bgprocess', [
|
|||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
content: '<div class="bg-process-details">' +
|
content: '<div class="bg-process-details">' +
|
||||||
'<p class="bg-detailed-desc"></p>' +
|
'<p class="bg-detailed-desc"></p>' +
|
||||||
'<div class="bg-process-stats">' +
|
'<div class="bg-process-stats" style="padding-bottom: 5px">' +
|
||||||
|
'<div class="row align-items-center">' +
|
||||||
|
'<div class="col-9">' +
|
||||||
'<span><b>' + gettext('Start time') + ': </b>' +
|
'<span><b>' + gettext('Start time') + ': </b>' +
|
||||||
'<span class="bgprocess-start-time"></span>' +
|
'<span class="bgprocess-start-time"></span>' +
|
||||||
'</span></div>' +
|
'</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="col-3">' +
|
||||||
|
'<button type="button" class="btn btn-danger btn-sm float-right bg-process-stop">' +
|
||||||
|
gettext('Stop Process') + '</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div class="bg-process-watcher">' +
|
'<div class="bg-process-watcher">' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
|
@ -29,7 +29,7 @@ from flask_sqlalchemy import SQLAlchemy
|
|||||||
#
|
#
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
SCHEMA_VERSION = 19
|
SCHEMA_VERSION = 20
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
#
|
#
|
||||||
@ -258,6 +258,7 @@ class Process(db.Model):
|
|||||||
end_time = db.Column(db.String(), nullable=True)
|
end_time = db.Column(db.String(), nullable=True)
|
||||||
exit_code = db.Column(db.Integer(), nullable=True)
|
exit_code = db.Column(db.Integer(), nullable=True)
|
||||||
acknowledge = db.Column(db.String(), nullable=True)
|
acknowledge = db.Column(db.String(), nullable=True)
|
||||||
|
utility_pid = db.Column(db.Integer, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
class Keys(db.Model):
|
class Keys(db.Model):
|
||||||
|
@ -19,7 +19,7 @@ from flask_babelex import gettext as _
|
|||||||
from flask_security import login_required, current_user
|
from flask_security import login_required, current_user
|
||||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||||
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
||||||
fs_short_path, document_dir
|
fs_short_path, document_dir, is_utility_exists
|
||||||
from pgadmin.utils.ajax import make_json_response, bad_request
|
from pgadmin.utils.ajax import make_json_response, bad_request
|
||||||
|
|
||||||
from config import PG_DEFAULT_DRIVER
|
from config import PG_DEFAULT_DRIVER
|
||||||
@ -63,7 +63,8 @@ class BackupModule(PgAdminModule):
|
|||||||
Returns:
|
Returns:
|
||||||
list: URL endpoints for backup module
|
list: URL endpoints for backup module
|
||||||
"""
|
"""
|
||||||
return ['backup.create_server_job', 'backup.create_object_job']
|
return ['backup.create_server_job', 'backup.create_object_job',
|
||||||
|
'backup.utility_exists']
|
||||||
|
|
||||||
|
|
||||||
# Create blueprint for BackupModule class
|
# Create blueprint for BackupModule class
|
||||||
@ -320,6 +321,13 @@ def create_backup_objects_job(sid):
|
|||||||
utility = manager.utility('backup') if backup_obj_type == 'objects' \
|
utility = manager.utility('backup') if backup_obj_type == 'objects' \
|
||||||
else manager.utility('backup_server')
|
else manager.utility('backup_server')
|
||||||
|
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
'--file',
|
'--file',
|
||||||
backup_file,
|
backup_file,
|
||||||
@ -461,3 +469,44 @@ def create_backup_objects_job(sid):
|
|||||||
return make_json_response(
|
return make_json_response(
|
||||||
data={'job_id': jid, 'Success': 1}
|
data={'job_id': jid, 'Success': 1}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route(
|
||||||
|
'/utility_exists/<int:sid>/<backup_obj_type>', endpoint='utility_exists'
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def check_utility_exists(sid, backup_obj_type):
|
||||||
|
"""
|
||||||
|
This function checks the utility file exist on the given path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sid: Server ID
|
||||||
|
backup_obj_type: Type of the object
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
server = Server.query.filter_by(
|
||||||
|
id=sid, user_id=current_user.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if server is None:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=_("Could not find the specified server.")
|
||||||
|
)
|
||||||
|
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
driver = get_driver(PG_DEFAULT_DRIVER)
|
||||||
|
manager = driver.connection_manager(server.id)
|
||||||
|
|
||||||
|
utility = manager.utility('backup') if backup_obj_type == 'objects' \
|
||||||
|
else manager.utility('backup_server')
|
||||||
|
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
|
return make_json_response(success=1)
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
import gettext from '../../../../static/js/gettext';
|
import gettext from '../../../../static/js/gettext';
|
||||||
import Backform from '../../../../static/js/backform.pgadmin';
|
import Backform from '../../../../static/js/backform.pgadmin';
|
||||||
import {Dialog} from '../../../../static/js/alertify/dialog';
|
import {Dialog} from '../../../../static/js/alertify/dialog';
|
||||||
|
import url_for from 'sources/url_for';
|
||||||
|
import axios from 'axios/index';
|
||||||
|
|
||||||
export class BackupDialog extends Dialog {
|
export class BackupDialog extends Dialog {
|
||||||
constructor(pgBrowser, $, alertify, BackupModel, backform = Backform) {
|
constructor(pgBrowser, $, alertify, BackupModel, backform = Backform) {
|
||||||
@ -19,6 +21,13 @@ export class BackupDialog extends Dialog {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url_for_utility_exists(id, params){
|
||||||
|
return url_for('backup.utility_exists', {
|
||||||
|
'sid': id,
|
||||||
|
'backup_obj_type': params == null ? 'objects' : 'servers',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
draw(action, aciTreeItem, params) {
|
draw(action, aciTreeItem, params) {
|
||||||
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
||||||
|
|
||||||
@ -30,17 +39,40 @@ export class BackupDialog extends Dialog {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeOfDialog = BackupDialog.typeOfDialog(params);
|
const baseUrl = this.url_for_utility_exists(serverInformation._id, params);
|
||||||
|
// Check pg_dump or pg_dumpall utility exists or not.
|
||||||
if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
let that = this;
|
||||||
|
let service = axios.create({});
|
||||||
|
service.get(
|
||||||
|
baseUrl
|
||||||
|
).then(function(res) {
|
||||||
|
if (!res.data.success) {
|
||||||
|
that.alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
res.data.errormsg
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialog = this.createOrGetDialog(
|
const typeOfDialog = BackupDialog.typeOfDialog(params);
|
||||||
|
|
||||||
|
if (!that.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialog = that.createOrGetDialog(
|
||||||
BackupDialog.dialogTitle(typeOfDialog),
|
BackupDialog.dialogTitle(typeOfDialog),
|
||||||
typeOfDialog
|
typeOfDialog
|
||||||
);
|
);
|
||||||
|
|
||||||
dialog(true).resizeTo('60%', '50%');
|
dialog(true).resizeTo('60%', '50%');
|
||||||
|
}).catch(function() {
|
||||||
|
that.alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
gettext('Failed to fetch Utility information')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static typeOfDialog(params) {
|
static typeOfDialog(params) {
|
||||||
|
@ -147,9 +147,16 @@ export class BackupDialogWrapper extends DialogWrapper {
|
|||||||
service.post(
|
service.post(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
this.view.model.toJSON()
|
this.view.model.toJSON()
|
||||||
).then(function () {
|
).then(function (res) {
|
||||||
|
if (res.data.success) {
|
||||||
dialog.alertify.success(gettext('Backup job created.'), 5);
|
dialog.alertify.success(gettext('Backup job created.'), 5);
|
||||||
dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog);
|
dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog);
|
||||||
|
} else {
|
||||||
|
dialog.alertify.alert(
|
||||||
|
gettext('Backup job creation failed.'),
|
||||||
|
res.data.errormsg
|
||||||
|
);
|
||||||
|
}
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
try {
|
try {
|
||||||
const err = error.response.data;
|
const err = error.response.data;
|
||||||
|
@ -10,10 +10,11 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
import os
|
||||||
|
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from pgadmin.utils import server_utils as server_utils
|
from pgadmin.utils import server_utils as server_utils, is_utility_exists
|
||||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||||
database_utils
|
database_utils
|
||||||
|
|
||||||
@ -639,13 +640,22 @@ class BackupCreateJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or \
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']],
|
||||||
|
'pg_dump')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
@patch('pgadmin.tools.backup.Server')
|
@patch('pgadmin.tools.backup.Server')
|
||||||
@patch('pgadmin.tools.backup.BackupMessage')
|
@patch('pgadmin.tools.backup.BackupMessage')
|
||||||
@patch('pgadmin.tools.backup.filename_with_file_manager_path')
|
@patch('pgadmin.tools.backup.filename_with_file_manager_path')
|
||||||
|
@ -11,6 +11,7 @@ import os
|
|||||||
|
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
|
from pgadmin.utils import is_utility_exists
|
||||||
import pgadmin.tools.backup.tests.test_backup_utils as backup_utils
|
import pgadmin.tools.backup.tests.test_backup_utils as backup_utils
|
||||||
|
|
||||||
|
|
||||||
@ -38,13 +39,22 @@ class BackupJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or\
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']],
|
||||||
|
'pg_dump')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
self.server_id = parent_node_dict["server"][-1]["server_id"]
|
self.server_id = parent_node_dict["server"][-1]["server_id"]
|
||||||
url = self.url.format(self.server_id)
|
url = self.url.format(self.server_id)
|
||||||
|
@ -17,7 +17,7 @@ from flask_babelex import gettext as _
|
|||||||
from flask_security import login_required, current_user
|
from flask_security import login_required, current_user
|
||||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||||
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
||||||
fs_short_path, document_dir, IS_WIN
|
fs_short_path, document_dir, IS_WIN, is_utility_exists
|
||||||
from pgadmin.utils.ajax import make_json_response, bad_request
|
from pgadmin.utils.ajax import make_json_response, bad_request
|
||||||
|
|
||||||
from config import PG_DEFAULT_DRIVER
|
from config import PG_DEFAULT_DRIVER
|
||||||
@ -58,7 +58,7 @@ class ImportExportModule(PgAdminModule):
|
|||||||
Returns:
|
Returns:
|
||||||
list: URL endpoints for backup module
|
list: URL endpoints for backup module
|
||||||
"""
|
"""
|
||||||
return ['import_export.create_job']
|
return ['import_export.create_job', 'import_export.utility_exists']
|
||||||
|
|
||||||
|
|
||||||
blueprint = ImportExportModule(MODULE_NAME, __name__)
|
blueprint = ImportExportModule(MODULE_NAME, __name__)
|
||||||
@ -231,6 +231,12 @@ def create_import_export_job(sid):
|
|||||||
|
|
||||||
# Get the utility path from the connection manager
|
# Get the utility path from the connection manager
|
||||||
utility = manager.utility('sql')
|
utility = manager.utility('sql')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
# Get the storage path from preference
|
# Get the storage path from preference
|
||||||
storage_dir = get_storage_directory()
|
storage_dir = get_storage_directory()
|
||||||
@ -323,3 +329,41 @@ def create_import_export_job(sid):
|
|||||||
return make_json_response(
|
return make_json_response(
|
||||||
data={'job_id': jid, 'success': 1}
|
data={'job_id': jid, 'success': 1}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route(
|
||||||
|
'/utility_exists/<int:sid>', endpoint='utility_exists'
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def check_utility_exists(sid):
|
||||||
|
"""
|
||||||
|
This function checks the utility file exist on the given path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sid: Server ID
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
server = Server.query.filter_by(
|
||||||
|
id=sid, user_id=current_user.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if server is None:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=_("Could not find the specified server.")
|
||||||
|
)
|
||||||
|
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
driver = get_driver(PG_DEFAULT_DRIVER)
|
||||||
|
manager = driver.connection_manager(server.id)
|
||||||
|
|
||||||
|
utility = manager.utility('sql')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
|
return make_json_response(success=1)
|
||||||
|
@ -534,6 +534,11 @@ Backform, commonUtils, supportedNodes
|
|||||||
if (res.success) {
|
if (res.success) {
|
||||||
Alertify.success(gettext('Import/export job created.'), 5);
|
Alertify.success(gettext('Import/export job created.'), 5);
|
||||||
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
||||||
|
} else {
|
||||||
|
Alertify.alert(
|
||||||
|
gettext('Import/export job creation failed.'),
|
||||||
|
res.errormsg
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(function(xhr) {
|
.fail(function(xhr) {
|
||||||
@ -652,12 +657,38 @@ Backform, commonUtils, supportedNodes
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const baseUrl = url_for('import_export.utility_exists', {
|
||||||
|
'sid': server_data._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check psql utility exists or not.
|
||||||
|
$.ajax({
|
||||||
|
url: baseUrl,
|
||||||
|
type:'GET',
|
||||||
|
})
|
||||||
|
.done(function(res) {
|
||||||
|
if (!res.success) {
|
||||||
|
Alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
res.errormsg
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Open the Alertify dialog for the import/export module
|
// Open the Alertify dialog for the import/export module
|
||||||
Alertify.ImportDialog(
|
Alertify.ImportDialog(
|
||||||
S(
|
S(
|
||||||
gettext('Import/Export data - table \'%s\'')
|
gettext('Import/Export data - table \'%s\'')
|
||||||
).sprintf(treeInfo.table.label).value(), node, i, d
|
).sprintf(treeInfo.table.label).value(), node, i, d
|
||||||
).set('resizable', true).resizeTo('70%', '80%');
|
).set('resizable', true).resizeTo('70%', '80%');
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
Alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
gettext('Failed to fetch Utility information')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ import simplejson as json
|
|||||||
|
|
||||||
from flask import url_for, Response, render_template, request, current_app
|
from flask import url_for, Response, render_template, request, current_app
|
||||||
from flask_babelex import gettext as _
|
from flask_babelex import gettext as _
|
||||||
from flask_security import login_required
|
from flask_security import login_required, current_user
|
||||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||||
from pgadmin.utils import PgAdminModule, html
|
from pgadmin.utils import PgAdminModule, html, is_utility_exists
|
||||||
from pgadmin.utils.ajax import bad_request, make_json_response
|
from pgadmin.utils.ajax import bad_request, make_json_response
|
||||||
from pgadmin.utils.driver import get_driver
|
from pgadmin.utils.driver import get_driver
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ class MaintenanceModule(PgAdminModule):
|
|||||||
Returns:
|
Returns:
|
||||||
list: URL endpoints for backup module
|
list: URL endpoints for backup module
|
||||||
"""
|
"""
|
||||||
return ['maintenance.create_job']
|
return ['maintenance.create_job', 'maintenance.utility_exists']
|
||||||
|
|
||||||
|
|
||||||
blueprint = MaintenanceModule(MODULE_NAME, __name__)
|
blueprint = MaintenanceModule(MODULE_NAME, __name__)
|
||||||
@ -214,6 +214,12 @@ def create_maintenance_job(sid, did):
|
|||||||
)
|
)
|
||||||
|
|
||||||
utility = manager.utility('sql')
|
utility = manager.utility('sql')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
# Create the command for the vacuum operation
|
# Create the command for the vacuum operation
|
||||||
query = render_template(
|
query = render_template(
|
||||||
@ -262,3 +268,41 @@ def create_maintenance_job(sid, did):
|
|||||||
data={'job_id': jid, 'status': True,
|
data={'job_id': jid, 'status': True,
|
||||||
'info': 'Maintenance job created.'}
|
'info': 'Maintenance job created.'}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route(
|
||||||
|
'/utility_exists/<int:sid>', endpoint='utility_exists'
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def check_utility_exists(sid):
|
||||||
|
"""
|
||||||
|
This function checks the utility file exist on the given path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sid: Server ID
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
server = Server.query.filter_by(
|
||||||
|
id=sid, user_id=current_user.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if server is None:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=_("Could not find the specified server.")
|
||||||
|
)
|
||||||
|
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
driver = get_driver(PG_DEFAULT_DRIVER)
|
||||||
|
manager = driver.connection_manager(server.id)
|
||||||
|
|
||||||
|
utility = manager.utility('sql')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
|
return make_json_response(success=1)
|
||||||
|
@ -392,7 +392,10 @@ define([
|
|||||||
Alertify.success(res.data.info);
|
Alertify.success(res.data.info);
|
||||||
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
||||||
} else {
|
} else {
|
||||||
Alertify.error(res.data.errmsg);
|
Alertify.alert(
|
||||||
|
gettext('Maintenance job creation failed.'),
|
||||||
|
res.errormsg
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(function() {
|
.fail(function() {
|
||||||
@ -467,8 +470,33 @@ define([
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const baseUrl = url_for('maintenance.utility_exists', {
|
||||||
|
'sid': server_data._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check psql utility exists or not.
|
||||||
|
$.ajax({
|
||||||
|
url: baseUrl,
|
||||||
|
type:'GET',
|
||||||
|
})
|
||||||
|
.done(function(res) {
|
||||||
|
if (!res.success) {
|
||||||
|
Alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
res.errormsg
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Open the Alertify dialog
|
// Open the Alertify dialog
|
||||||
Alertify.MaintenanceDialog('Maintenance...').set('resizable', true).resizeTo('60%', '80%');
|
Alertify.MaintenanceDialog('Maintenance...').set('resizable', true).resizeTo('60%', '80%');
|
||||||
|
})
|
||||||
|
.fail(function() {
|
||||||
|
Alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
gettext('Failed to fetch Utility information')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
import os
|
||||||
|
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
|
from pgadmin.utils import is_utility_exists
|
||||||
|
|
||||||
|
|
||||||
class MaintenanceJobTest(BaseTestGenerator):
|
class MaintenanceJobTest(BaseTestGenerator):
|
||||||
@ -38,13 +40,21 @@ class MaintenanceJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or\
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']], 'psql')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
self.server_id = parent_node_dict["database"][-1]["server_id"]
|
self.server_id = parent_node_dict["database"][-1]["server_id"]
|
||||||
self.db_id = parent_node_dict["database"][-1]["db_id"]
|
self.db_id = parent_node_dict["database"][-1]["db_id"]
|
||||||
|
@ -8,11 +8,12 @@
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from pgadmin.utils import server_utils as server_utils
|
from pgadmin.utils import server_utils as server_utils, is_utility_exists
|
||||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||||
database_utils
|
database_utils
|
||||||
|
|
||||||
@ -128,13 +129,21 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or\
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']], 'psql')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
@patch('pgadmin.tools.maintenance.Server')
|
@patch('pgadmin.tools.maintenance.Server')
|
||||||
@patch('pgadmin.tools.maintenance.Message')
|
@patch('pgadmin.tools.maintenance.Message')
|
||||||
@patch('pgadmin.tools.maintenance.BatchProcess')
|
@patch('pgadmin.tools.maintenance.BatchProcess')
|
||||||
|
@ -18,7 +18,7 @@ from flask_babelex import gettext as _
|
|||||||
from flask_security import login_required, current_user
|
from flask_security import login_required, current_user
|
||||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||||
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
from pgadmin.utils import PgAdminModule, get_storage_directory, html, \
|
||||||
fs_short_path, document_dir
|
fs_short_path, document_dir, is_utility_exists
|
||||||
from pgadmin.utils.ajax import make_json_response, bad_request
|
from pgadmin.utils.ajax import make_json_response, bad_request
|
||||||
|
|
||||||
from config import PG_DEFAULT_DRIVER
|
from config import PG_DEFAULT_DRIVER
|
||||||
@ -56,7 +56,7 @@ class RestoreModule(PgAdminModule):
|
|||||||
Returns:
|
Returns:
|
||||||
list: URL endpoints for backup module
|
list: URL endpoints for backup module
|
||||||
"""
|
"""
|
||||||
return ['restore.create_job']
|
return ['restore.create_job', 'restore.utility_exists']
|
||||||
|
|
||||||
|
|
||||||
# Create blueprint for RestoreModule class
|
# Create blueprint for RestoreModule class
|
||||||
@ -231,6 +231,12 @@ def create_restore_job(sid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
utility = manager.utility('restore')
|
utility = manager.utility('restore')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
@ -365,7 +371,39 @@ def create_restore_job(sid):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route(
|
||||||
|
'/utility_exists/<int:sid>', endpoint='utility_exists'
|
||||||
|
)
|
||||||
|
@login_required
|
||||||
|
def check_utility_exists(sid):
|
||||||
"""
|
"""
|
||||||
TODO://
|
This function checks the utility file exist on the given path.
|
||||||
Add browser tree
|
|
||||||
|
Args:
|
||||||
|
sid: Server ID
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
|
server = Server.query.filter_by(
|
||||||
|
id=sid, user_id=current_user.id
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if server is None:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=_("Could not find the specified server.")
|
||||||
|
)
|
||||||
|
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
driver = get_driver(PG_DEFAULT_DRIVER)
|
||||||
|
manager = driver.connection_manager(server.id)
|
||||||
|
|
||||||
|
utility = manager.utility('restore')
|
||||||
|
ret_val = is_utility_exists(utility)
|
||||||
|
if ret_val:
|
||||||
|
return make_json_response(
|
||||||
|
success=0,
|
||||||
|
errormsg=ret_val
|
||||||
|
)
|
||||||
|
|
||||||
|
return make_json_response(success=1)
|
||||||
|
@ -11,6 +11,8 @@ import gettext from '../../../../static/js/gettext';
|
|||||||
import {sprintf} from 'sprintf-js';
|
import {sprintf} from 'sprintf-js';
|
||||||
import Backform from '../../../../static/js/backform.pgadmin';
|
import Backform from '../../../../static/js/backform.pgadmin';
|
||||||
import {Dialog} from '../../../../static/js/alertify/dialog';
|
import {Dialog} from '../../../../static/js/alertify/dialog';
|
||||||
|
import url_for from 'sources/url_for';
|
||||||
|
import axios from 'axios/index';
|
||||||
|
|
||||||
export class RestoreDialog extends Dialog {
|
export class RestoreDialog extends Dialog {
|
||||||
constructor(pgBrowser, $, alertify, RestoreModel, backform = Backform) {
|
constructor(pgBrowser, $, alertify, RestoreModel, backform = Backform) {
|
||||||
@ -19,6 +21,12 @@ export class RestoreDialog extends Dialog {
|
|||||||
pgBrowser, $, alertify, RestoreModel, backform);
|
pgBrowser, $, alertify, RestoreModel, backform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
url_for_utility_exists(id){
|
||||||
|
return url_for('restore.utility_exists', {
|
||||||
|
'sid': id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
draw(action, aciTreeItem) {
|
draw(action, aciTreeItem) {
|
||||||
|
|
||||||
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
||||||
@ -31,23 +39,43 @@ export class RestoreDialog extends Dialog {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
const baseUrl = this.url_for_utility_exists(serverInformation._id);
|
||||||
|
// Check pg_restore utility exists or not.
|
||||||
|
let that = this;
|
||||||
|
let service = axios.create({});
|
||||||
|
service.get(
|
||||||
|
baseUrl
|
||||||
|
).then(function(res) {
|
||||||
|
if (!res.data.success) {
|
||||||
|
that.alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
res.data.errormsg
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let aciTreeItem1 = aciTreeItem || this.pgBrowser.treeMenu.selected();
|
if (!that.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
||||||
let item = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let aciTreeItem1 = aciTreeItem || that.pgBrowser.treeMenu.selected();
|
||||||
|
let item = that.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
|
||||||
const data = item.getData();
|
const data = item.getData();
|
||||||
const node = this.pgBrowser.Nodes[data._type];
|
const node = that.pgBrowser.Nodes[data._type];
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
|
let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
|
||||||
|
that.createOrGetDialog(title, 'restore');
|
||||||
this.createOrGetDialog(title, 'restore');
|
that.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
|
||||||
|
}).catch(function() {
|
||||||
this.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
|
that.alertify.alert(
|
||||||
|
gettext('Utility not found'),
|
||||||
|
gettext('Failed to fetch Utility information')
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogName() {
|
dialogName() {
|
||||||
|
@ -136,9 +136,16 @@ export class RestoreDialogWrapper extends DialogWrapper {
|
|||||||
service.post(
|
service.post(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
this.view.model.toJSON()
|
this.view.model.toJSON()
|
||||||
).then(function () {
|
).then(function (res) {
|
||||||
|
if (res.data.success) {
|
||||||
dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
|
dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
|
||||||
dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
|
dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
|
||||||
|
} else {
|
||||||
|
dialogWrapper.alertify.alert(
|
||||||
|
gettext('Restore job creation failed.'),
|
||||||
|
res.data.errormsg
|
||||||
|
);
|
||||||
|
}
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
try {
|
try {
|
||||||
const err = error.response.data;
|
const err = error.response.data;
|
||||||
|
@ -17,7 +17,7 @@ import simplejson as json
|
|||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from regression.python_test_utils import test_utils as utils
|
from regression.python_test_utils import test_utils as utils
|
||||||
from pgadmin.utils import server_utils as server_utils
|
from pgadmin.utils import server_utils as server_utils, is_utility_exists
|
||||||
import pgadmin.tools.backup.tests.test_backup_utils as backup_utils
|
import pgadmin.tools.backup.tests.test_backup_utils as backup_utils
|
||||||
|
|
||||||
|
|
||||||
@ -62,13 +62,22 @@ class RestoreJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or\
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']],
|
||||||
|
'pg_restore')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
def create_backup(self):
|
def create_backup(self):
|
||||||
url = self.backup_options['url'].format(self.server_id)
|
url = self.backup_options['url'].format(self.server_id)
|
||||||
job_id = backup_utils.create_backup_job(self.tester, url,
|
job_id = backup_utils.create_backup_job(self.tester, url,
|
||||||
|
@ -9,10 +9,11 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
|
import os
|
||||||
|
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
from regression import parent_node_dict
|
from regression import parent_node_dict
|
||||||
from pgadmin.utils import server_utils as server_utils
|
from pgadmin.utils import server_utils as server_utils, is_utility_exists
|
||||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||||
database_utils
|
database_utils
|
||||||
|
|
||||||
@ -291,13 +292,22 @@ class RestoreCreateJobTest(BaseTestGenerator):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if self.server['default_binary_paths'] is None:
|
if 'default_binary_paths' not in self.server or \
|
||||||
|
self.server['type'] not in self.server['default_binary_paths'] or\
|
||||||
|
self.server['default_binary_paths'][self.server['type']] == '':
|
||||||
self.skipTest(
|
self.skipTest(
|
||||||
"default_binary_paths is not set for the server {0}".format(
|
"default_binary_paths is not set for the server {0}".format(
|
||||||
self.server['name']
|
self.server['name']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binary_path = os.path.join(
|
||||||
|
self.server['default_binary_paths'][self.server['type']],
|
||||||
|
'pg_restore')
|
||||||
|
retVal = is_utility_exists(binary_path)
|
||||||
|
if retVal is not None:
|
||||||
|
self.skipTest(retVal)
|
||||||
|
|
||||||
@patch('pgadmin.tools.restore.Server')
|
@patch('pgadmin.tools.restore.Server')
|
||||||
@patch('pgadmin.tools.restore.current_user')
|
@patch('pgadmin.tools.restore.current_user')
|
||||||
@patch('pgadmin.tools.restore.RestoreMessage')
|
@patch('pgadmin.tools.restore.RestoreMessage')
|
||||||
|
@ -284,6 +284,18 @@ def get_complete_file_path(file):
|
|||||||
return file if os.path.isfile(file) else None
|
return file if os.path.isfile(file) else None
|
||||||
|
|
||||||
|
|
||||||
|
def is_utility_exists(file):
|
||||||
|
"""
|
||||||
|
This function will check the utility file exists on given path.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
error_msg = None
|
||||||
|
if not os.path.exists(file):
|
||||||
|
error_msg = gettext(u"'%s' file not found. Please correct the Binary"
|
||||||
|
u" Path in the Preferences dialog" % file)
|
||||||
|
return error_msg
|
||||||
|
|
||||||
|
|
||||||
# Shortcut configuration for Accesskey
|
# Shortcut configuration for Accesskey
|
||||||
ACCESSKEY_FIELDS = [
|
ACCESSKEY_FIELDS = [
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
||||||
import {TreeFake} from '../tree/tree_fake';
|
import {TreeFake} from '../tree/tree_fake';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import axios from 'axios/index';
|
||||||
|
|
||||||
const context = describe;
|
const context = describe;
|
||||||
|
|
||||||
@ -93,7 +95,9 @@ describe('BackupDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#draw', () => {
|
describe('#draw', () => {
|
||||||
|
let networkMock;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
networkMock = new MockAdapter(axios);
|
||||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||||
alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects');
|
alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects');
|
||||||
backupDialog = new BackupDialog(
|
backupDialog = new BackupDialog(
|
||||||
@ -106,6 +110,10 @@ describe('BackupDialog', () => {
|
|||||||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
networkMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
context('there are no ancestors of the type server', () => {
|
context('there are no ancestors of the type server', () => {
|
||||||
it('does not create a dialog', () => {
|
it('does not create a dialog', () => {
|
||||||
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
||||||
@ -183,19 +191,27 @@ describe('BackupDialog', () => {
|
|||||||
alertifySpy['backup_objects'].and
|
alertifySpy['backup_objects'].and
|
||||||
.returnValue(backupDialogResizeToSpy);
|
.returnValue(backupDialogResizeToSpy);
|
||||||
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
||||||
|
spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/objects');
|
||||||
|
networkMock.onGet('/backup/utility_exists/10/objects').reply(200, {'success': 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays the dialog', () => {
|
it('displays the dialog', (done) => {
|
||||||
backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
|
backupDialog.draw(null, [{id: 'serverTreeNode'}], null);
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
|
expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
|
||||||
expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
context('database label contain "="', () => {
|
context('database label contain "="', () => {
|
||||||
it('should create alert dialog with backup error', () => {
|
it('should create alert dialog with backup error', (done) => {
|
||||||
backupDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
|
backupDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
|
expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
|
||||||
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -385,7 +385,7 @@ describe('BackupDialogWrapper', () => {
|
|||||||
|
|
||||||
networkMock.onPost('/backup/job/10').reply((request) => {
|
networkMock.onPost('/backup/job/10').reply((request) => {
|
||||||
dataSentToServer = request.data;
|
dataSentToServer = request.data;
|
||||||
return [200, {}];
|
return [200, {'success': 1}];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -485,7 +485,7 @@ describe('BackupDialogWrapper', () => {
|
|||||||
|
|
||||||
networkMock.onPost('/backup/job/10/object').reply((request) => {
|
networkMock.onPost('/backup/job/10/object').reply((request) => {
|
||||||
dataSentToServer = request.data;
|
dataSentToServer = request.data;
|
||||||
return [200, {}];
|
return [200, {'success': 1}];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
||||||
import {TreeFake} from '../tree/tree_fake';
|
import {TreeFake} from '../tree/tree_fake';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import axios from 'axios/index';
|
||||||
|
|
||||||
const context = describe;
|
const context = describe;
|
||||||
|
|
||||||
@ -48,7 +50,9 @@ describe('GlobalServerBackupDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#draw', () => {
|
describe('#draw', () => {
|
||||||
|
let networkMock;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
networkMock = new MockAdapter(axios);
|
||||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||||
alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals');
|
alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals');
|
||||||
alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server');
|
alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server');
|
||||||
@ -62,6 +66,10 @@ describe('GlobalServerBackupDialog', () => {
|
|||||||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
networkMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
context('there are no ancestors of the type server', () => {
|
context('there are no ancestors of the type server', () => {
|
||||||
it('does not create a dialog', () => {
|
it('does not create a dialog', () => {
|
||||||
pgBrowser.treeMenu.selectNode([{id: 'level1'}]);
|
pgBrowser.treeMenu.selectNode([{id: 'level1'}]);
|
||||||
@ -144,21 +152,29 @@ describe('GlobalServerBackupDialog', () => {
|
|||||||
alertifySpy['BackupDialog_server'].and
|
alertifySpy['BackupDialog_server'].and
|
||||||
.returnValue(serverResizeToSpy);
|
.returnValue(serverResizeToSpy);
|
||||||
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
||||||
|
spyOn(backupDialog, 'url_for_utility_exists').and.returnValue('/backup/utility_exists/10/servers');
|
||||||
|
networkMock.onGet('/backup/utility_exists/10/servers').reply(200, {'success': 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('dialog for global backup', () => {
|
context('dialog for global backup', () => {
|
||||||
it('displays the dialog', () => {
|
it('displays the dialog', (done) => {
|
||||||
backupDialog.draw(null, [serverTreeNode], {globals: true});
|
backupDialog.draw(null, [serverTreeNode], {globals: true});
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
|
expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
|
||||||
expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('dialog for server backup', () => {
|
context('dialog for server backup', () => {
|
||||||
it('displays the dialog', () => {
|
it('displays the dialog', (done) => {
|
||||||
backupDialog.draw(null, [serverTreeNode], {server: true});
|
backupDialog.draw(null, [serverTreeNode], {server: true});
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
|
expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
|
||||||
expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
import {TreeFake} from '../tree/tree_fake';
|
import {TreeFake} from '../tree/tree_fake';
|
||||||
import {RestoreDialog} from '../../../pgadmin/tools/restore/static/js/restore_dialog';
|
import {RestoreDialog} from '../../../pgadmin/tools/restore/static/js/restore_dialog';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import axios from 'axios/index';
|
||||||
|
|
||||||
const context = describe;
|
const context = describe;
|
||||||
|
|
||||||
@ -81,7 +83,9 @@ describe('RestoreDialog', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#draw', () => {
|
describe('#draw', () => {
|
||||||
|
let networkMock;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
networkMock = new MockAdapter(axios);
|
||||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||||
alertifySpy['pg_restore'] = jasmine.createSpy('pg_restore');
|
alertifySpy['pg_restore'] = jasmine.createSpy('pg_restore');
|
||||||
restoreDialog = new RestoreDialog(
|
restoreDialog = new RestoreDialog(
|
||||||
@ -94,6 +98,10 @@ describe('RestoreDialog', () => {
|
|||||||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
networkMock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
context('there are no ancestors of the type server', () => {
|
context('there are no ancestors of the type server', () => {
|
||||||
it('does not create a dialog', () => {
|
it('does not create a dialog', () => {
|
||||||
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
||||||
@ -172,10 +180,13 @@ describe('RestoreDialog', () => {
|
|||||||
.returnValue(spy);
|
.returnValue(spy);
|
||||||
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
||||||
pgBrowser.Nodes.server.label = 'some-server-label';
|
pgBrowser.Nodes.server.label = 'some-server-label';
|
||||||
|
spyOn(restoreDialog, 'url_for_utility_exists').and.returnValue('/restore/utility_exists/10/objects');
|
||||||
|
networkMock.onGet('/restore/utility_exists/10/objects').reply(200, {'success': 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays the dialog', () => {
|
it('displays the dialog', (done) => {
|
||||||
restoreDialog.draw(null, [{id: 'serverTreeNode'}], {server: true});
|
restoreDialog.draw(null, [{id: 'serverTreeNode'}], {server: true});
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
|
expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
|
||||||
'Restore (some-server-label: some-tree-label)',
|
'Restore (some-server-label: some-tree-label)',
|
||||||
[{id: 'serverTreeNode'}],
|
[{id: 'serverTreeNode'}],
|
||||||
@ -187,13 +198,18 @@ describe('RestoreDialog', () => {
|
|||||||
pgBrowser.Nodes.server
|
pgBrowser.Nodes.server
|
||||||
);
|
);
|
||||||
expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
|
expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
context('database label contain "="', () => {
|
context('database label contain "="', () => {
|
||||||
it('should create alert dialog with restore error', () => {
|
it('should create alert dialog with restore error', (done) => {
|
||||||
restoreDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
|
restoreDialog.draw(null, [{id: 'database_with_equal_in_name'}], null);
|
||||||
|
setTimeout(() => {
|
||||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
|
expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
|
||||||
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
||||||
|
done();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -397,7 +397,7 @@ describe('RestoreDialogWrapper', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
networkMock.onPost('/restore/job/10').reply((request) => {
|
networkMock.onPost('/restore/job/10').reply((request) => {
|
||||||
dataSentToServer = request.data;
|
dataSentToServer = request.data;
|
||||||
return [200, {}];
|
return [200, {'success': 1}];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -436,6 +436,9 @@ def create_server(server):
|
|||||||
server_id = cur.lastrowid
|
server_id = cur.lastrowid
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
type = get_server_type(server)
|
||||||
|
server['type'] = type
|
||||||
# Add server info to parent_node_dict
|
# Add server info to parent_node_dict
|
||||||
regression.parent_node_dict["server"].append(
|
regression.parent_node_dict["server"].append(
|
||||||
{
|
{
|
||||||
@ -899,3 +902,35 @@ def create_schema(server, db_name, schema_name):
|
|||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc(file=sys.stderr)
|
traceback.print_exc(file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def get_server_type(server):
|
||||||
|
"""
|
||||||
|
This function will return the type of the server (PPAS, PG or GPDB)
|
||||||
|
:param server:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
connection = get_db_connection(
|
||||||
|
server['db'],
|
||||||
|
server['username'],
|
||||||
|
server['db_password'],
|
||||||
|
server['host'],
|
||||||
|
server['port'],
|
||||||
|
server['sslmode']
|
||||||
|
)
|
||||||
|
|
||||||
|
pg_cursor = connection.cursor()
|
||||||
|
# Get 'version' string
|
||||||
|
pg_cursor.execute("SELECT version()")
|
||||||
|
version_string = pg_cursor.fetchone()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
if "Greenplum Database" in version_string:
|
||||||
|
return 'gpdb'
|
||||||
|
elif "EnterpriseDB" in version_string:
|
||||||
|
return 'ppas'
|
||||||
|
|
||||||
|
return 'pg'
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc(file=sys.stderr)
|
||||||
|