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.pull/14/head
|
@ -113,6 +113,8 @@ When you’ve specified the details that will be incorporated into the pg_dump c
|
|||
.. image:: images/backup_messages.png
|
||||
: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.
|
||||
|
||||
.. 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
|
||||
: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.
|
||||
|
||||
.. 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
|
||||
: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.
|
||||
|
||||
.. 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
|
||||
: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:
|
||||
|
||||
.. image:: images/import_export_pw.png
|
||||
|
|
|
@ -34,6 +34,8 @@ pgAdmin will inform you when the background process completes:
|
|||
.. image:: images/maintenance_complete.png
|
||||
: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:
|
||||
|
||||
.. image:: images/maintenance_pw.png
|
||||
|
|
|
@ -17,6 +17,7 @@ Features
|
|||
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 #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.
|
||||
|
|
|
@ -84,6 +84,8 @@ When you’ve specified the details that will be incorporated into the pg_restor
|
|||
.. image:: images/restore_messages.png
|
||||
: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.
|
||||
|
||||
.. image:: images/restore_process_watcher.png
|
||||
|
|
|
@ -28,6 +28,7 @@ speaklater==1.3
|
|||
sqlparse==0.2.4
|
||||
WTForms==2.1
|
||||
Flask-Paranoid==0.2.0
|
||||
psutil==5.4.7
|
||||
|
||||
################################################################
|
||||
# Modules specifically requires for Python2.7 or greater version
|
||||
|
|
|
@ -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 [
|
||||
'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()
|
||||
except LookupError as 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(
|
||||
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...')
|
||||
# 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 os
|
||||
import sys
|
||||
import psutil
|
||||
from abc import ABCMeta, abstractproperty, abstractmethod
|
||||
from datetime import datetime
|
||||
from pickle import dumps, loads
|
||||
|
@ -523,6 +524,10 @@ class BatchProcess(object):
|
|||
if 'end_time' in data and 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
|
||||
|
||||
except ValueError as e:
|
||||
|
@ -657,3 +662,26 @@ class BatchProcess(object):
|
|||
|
||||
if 'env' in kwargs:
|
||||
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', {
|
||||
'pid': this.id,
|
||||
});
|
||||
case 'stop_process':
|
||||
return url_for('bgprocess.stop_process', {
|
||||
'pid': this.id,
|
||||
});
|
||||
default:
|
||||
return url_for('bgprocess.list');
|
||||
}
|
||||
|
@ -258,7 +262,22 @@ define('misc.bgprocess', [
|
|||
$('<div></div>', {
|
||||
class: 'pg-bg-start',
|
||||
}).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(
|
||||
$('<div class="pg-bg-etime"></div>')
|
||||
)
|
||||
|
@ -313,6 +332,9 @@ define('misc.bgprocess', [
|
|||
|
||||
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
|
||||
self.container.find('.pg-bg-etime').empty().append(
|
||||
|
@ -332,6 +354,14 @@ define('misc.bgprocess', [
|
|||
} else if (self.exit_code == 1) {
|
||||
$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 {
|
||||
self.show_detailed_view.apply(self);
|
||||
}
|
||||
|
@ -359,7 +389,18 @@ define('misc.bgprocess', [
|
|||
),
|
||||
$logs = container.find('.bg-process-watcher'),
|
||||
$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) {
|
||||
// set logs
|
||||
|
@ -439,6 +480,25 @@ define('misc.bgprocess', [
|
|||
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(
|
||||
|
@ -538,10 +598,18 @@ define('misc.bgprocess', [
|
|||
isPrivate: true,
|
||||
content: '<div class="bg-process-details">' +
|
||||
'<p class="bg-detailed-desc"></p>' +
|
||||
'<div class="bg-process-stats">' +
|
||||
'<span><b>' + gettext('Start time') + ': </b>' +
|
||||
'<span class="bgprocess-start-time"></span>' +
|
||||
'</span></div>' +
|
||||
'<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 class="bgprocess-start-time"></span>' +
|
||||
'</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 class="bg-process-watcher">' +
|
||||
'</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)
|
||||
exit_code = db.Column(db.Integer(), nullable=True)
|
||||
acknowledge = db.Column(db.String(), nullable=True)
|
||||
utility_pid = db.Column(db.Integer, nullable=False)
|
||||
|
||||
|
||||
class Keys(db.Model):
|
||||
|
|
|
@ -19,7 +19,7 @@ from flask_babelex import gettext as _
|
|||
from flask_security import login_required, current_user
|
||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||
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 config import PG_DEFAULT_DRIVER
|
||||
|
@ -63,7 +63,8 @@ class BackupModule(PgAdminModule):
|
|||
Returns:
|
||||
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
|
||||
|
@ -320,6 +321,13 @@ def create_backup_objects_job(sid):
|
|||
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
|
||||
)
|
||||
|
||||
args = [
|
||||
'--file',
|
||||
backup_file,
|
||||
|
@ -461,3 +469,44 @@ def create_backup_objects_job(sid):
|
|||
return make_json_response(
|
||||
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 Backform from '../../../../static/js/backform.pgadmin';
|
||||
import {Dialog} from '../../../../static/js/alertify/dialog';
|
||||
import url_for from 'sources/url_for';
|
||||
import axios from 'axios/index';
|
||||
|
||||
export class BackupDialog extends Dialog {
|
||||
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) {
|
||||
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
||||
|
||||
|
@ -30,17 +39,40 @@ export class BackupDialog extends Dialog {
|
|||
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.
|
||||
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;
|
||||
}
|
||||
|
||||
if (!this.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
||||
const typeOfDialog = BackupDialog.typeOfDialog(params);
|
||||
|
||||
if (!that.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dialog = that.createOrGetDialog(
|
||||
BackupDialog.dialogTitle(typeOfDialog),
|
||||
typeOfDialog
|
||||
);
|
||||
|
||||
dialog(true).resizeTo('60%', '50%');
|
||||
}).catch(function() {
|
||||
that.alertify.alert(
|
||||
gettext('Utility not found'),
|
||||
gettext('Failed to fetch Utility information')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const dialog = this.createOrGetDialog(
|
||||
BackupDialog.dialogTitle(typeOfDialog),
|
||||
typeOfDialog
|
||||
);
|
||||
dialog(true).resizeTo('60%', '50%');
|
||||
});
|
||||
}
|
||||
|
||||
static typeOfDialog(params) {
|
||||
|
|
|
@ -147,9 +147,16 @@ export class BackupDialogWrapper extends DialogWrapper {
|
|||
service.post(
|
||||
baseUrl,
|
||||
this.view.model.toJSON()
|
||||
).then(function () {
|
||||
dialog.alertify.success(gettext('Backup job created.'), 5);
|
||||
dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog);
|
||||
).then(function (res) {
|
||||
if (res.data.success) {
|
||||
dialog.alertify.success(gettext('Backup job created.'), 5);
|
||||
dialog.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialog);
|
||||
} else {
|
||||
dialog.alertify.alert(
|
||||
gettext('Backup job creation failed.'),
|
||||
res.data.errormsg
|
||||
);
|
||||
}
|
||||
}).catch(function (error) {
|
||||
try {
|
||||
const err = error.response.data;
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
|
||||
import sys
|
||||
import simplejson as json
|
||||
import os
|
||||
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
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 \
|
||||
database_utils
|
||||
|
||||
|
@ -639,13 +640,22 @@ class BackupCreateJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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.BackupMessage')
|
||||
@patch('pgadmin.tools.backup.filename_with_file_manager_path')
|
||||
|
|
|
@ -11,6 +11,7 @@ import os
|
|||
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from pgadmin.utils import is_utility_exists
|
||||
import pgadmin.tools.backup.tests.test_backup_utils as backup_utils
|
||||
|
||||
|
||||
|
@ -38,13 +39,22 @@ class BackupJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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):
|
||||
self.server_id = parent_node_dict["server"][-1]["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 pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||
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 config import PG_DEFAULT_DRIVER
|
||||
|
@ -58,7 +58,7 @@ class ImportExportModule(PgAdminModule):
|
|||
Returns:
|
||||
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__)
|
||||
|
@ -231,6 +231,12 @@ def create_import_export_job(sid):
|
|||
|
||||
# Get the utility path from the connection manager
|
||||
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
|
||||
storage_dir = get_storage_directory()
|
||||
|
@ -323,3 +329,41 @@ def create_import_export_job(sid):
|
|||
return make_json_response(
|
||||
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) {
|
||||
Alertify.success(gettext('Import/export job created.'), 5);
|
||||
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
||||
} else {
|
||||
Alertify.alert(
|
||||
gettext('Import/export job creation failed.'),
|
||||
res.errormsg
|
||||
);
|
||||
}
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
|
@ -652,12 +657,38 @@ Backform, commonUtils, supportedNodes
|
|||
});
|
||||
}
|
||||
|
||||
// Open the Alertify dialog for the import/export module
|
||||
Alertify.ImportDialog(
|
||||
S(
|
||||
gettext('Import/Export data - table \'%s\'')
|
||||
).sprintf(treeInfo.table.label).value(), node, i, d
|
||||
).set('resizable', true).resizeTo('70%', '80%');
|
||||
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
|
||||
Alertify.ImportDialog(
|
||||
S(
|
||||
gettext('Import/Export data - table \'%s\'')
|
||||
).sprintf(treeInfo.table.label).value(), node, i, d
|
||||
).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_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.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.driver import get_driver
|
||||
|
||||
|
@ -68,7 +68,7 @@ class MaintenanceModule(PgAdminModule):
|
|||
Returns:
|
||||
list: URL endpoints for backup module
|
||||
"""
|
||||
return ['maintenance.create_job']
|
||||
return ['maintenance.create_job', 'maintenance.utility_exists']
|
||||
|
||||
|
||||
blueprint = MaintenanceModule(MODULE_NAME, __name__)
|
||||
|
@ -214,6 +214,12 @@ def create_maintenance_job(sid, did):
|
|||
)
|
||||
|
||||
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
|
||||
query = render_template(
|
||||
|
@ -262,3 +268,41 @@ def create_maintenance_job(sid, did):
|
|||
data={'job_id': jid, 'status': True,
|
||||
'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);
|
||||
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
|
||||
} else {
|
||||
Alertify.error(res.data.errmsg);
|
||||
Alertify.alert(
|
||||
gettext('Maintenance job creation failed.'),
|
||||
res.errormsg
|
||||
);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
|
@ -467,8 +470,33 @@ define([
|
|||
});
|
||||
}
|
||||
|
||||
// Open the Alertify dialog
|
||||
Alertify.MaintenanceDialog('Maintenance...').set('resizable', true).resizeTo('60%', '80%');
|
||||
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
|
||||
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 random
|
||||
import simplejson as json
|
||||
import os
|
||||
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from pgadmin.utils import is_utility_exists
|
||||
|
||||
|
||||
class MaintenanceJobTest(BaseTestGenerator):
|
||||
|
@ -38,13 +40,21 @@ class MaintenanceJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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):
|
||||
self.server_id = parent_node_dict["database"][-1]["server_id"]
|
||||
self.db_id = parent_node_dict["database"][-1]["db_id"]
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
##########################################################################
|
||||
|
||||
import sys
|
||||
import os
|
||||
import simplejson as json
|
||||
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
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 \
|
||||
database_utils
|
||||
|
||||
|
@ -128,13 +129,21 @@ class MaintenanceCreateJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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.Message')
|
||||
@patch('pgadmin.tools.maintenance.BatchProcess')
|
||||
|
|
|
@ -18,7 +18,7 @@ from flask_babelex import gettext as _
|
|||
from flask_security import login_required, current_user
|
||||
from pgadmin.misc.bgprocess.processes import BatchProcess, IProcessDesc
|
||||
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 config import PG_DEFAULT_DRIVER
|
||||
|
@ -56,7 +56,7 @@ class RestoreModule(PgAdminModule):
|
|||
Returns:
|
||||
list: URL endpoints for backup module
|
||||
"""
|
||||
return ['restore.create_job']
|
||||
return ['restore.create_job', 'restore.utility_exists']
|
||||
|
||||
|
||||
# Create blueprint for RestoreModule class
|
||||
|
@ -231,6 +231,12 @@ def create_restore_job(sid):
|
|||
)
|
||||
|
||||
utility = manager.utility('restore')
|
||||
ret_val = is_utility_exists(utility)
|
||||
if ret_val:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=ret_val
|
||||
)
|
||||
|
||||
args = []
|
||||
|
||||
|
@ -365,7 +371,39 @@ def create_restore_job(sid):
|
|||
)
|
||||
|
||||
|
||||
"""
|
||||
TODO://
|
||||
Add browser tree
|
||||
"""
|
||||
@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('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 Backform from '../../../../static/js/backform.pgadmin';
|
||||
import {Dialog} from '../../../../static/js/alertify/dialog';
|
||||
import url_for from 'sources/url_for';
|
||||
import axios from 'axios/index';
|
||||
|
||||
export class RestoreDialog extends Dialog {
|
||||
constructor(pgBrowser, $, alertify, RestoreModel, backform = Backform) {
|
||||
|
@ -19,6 +21,12 @@ export class RestoreDialog extends Dialog {
|
|||
pgBrowser, $, alertify, RestoreModel, backform);
|
||||
}
|
||||
|
||||
url_for_utility_exists(id){
|
||||
return url_for('restore.utility_exists', {
|
||||
'sid': id,
|
||||
});
|
||||
}
|
||||
|
||||
draw(action, aciTreeItem) {
|
||||
|
||||
const serverInformation = this.retrieveAncestorOfTypeServer(aciTreeItem);
|
||||
|
@ -31,23 +39,43 @@ export class RestoreDialog extends Dialog {
|
|||
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;
|
||||
}
|
||||
|
||||
if (!that.canExecuteOnCurrentDatabase(aciTreeItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let aciTreeItem1 = aciTreeItem || that.pgBrowser.treeMenu.selected();
|
||||
let item = that.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
|
||||
const data = item.getData();
|
||||
const node = that.pgBrowser.Nodes[data._type];
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
|
||||
that.createOrGetDialog(title, 'restore');
|
||||
that.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
|
||||
}).catch(function() {
|
||||
that.alertify.alert(
|
||||
gettext('Utility not found'),
|
||||
gettext('Failed to fetch Utility information')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let aciTreeItem1 = aciTreeItem || this.pgBrowser.treeMenu.selected();
|
||||
let item = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem1);
|
||||
const data = item.getData();
|
||||
const node = this.pgBrowser.Nodes[data._type];
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
let title = sprintf(gettext('Restore (%s: %s)'), node.label, data.label);
|
||||
|
||||
this.createOrGetDialog(title, 'restore');
|
||||
|
||||
this.alertify.pg_restore(title, aciTreeItem1, data, node).resizeTo('65%', '60%');
|
||||
});
|
||||
}
|
||||
|
||||
dialogName() {
|
||||
|
|
|
@ -136,9 +136,16 @@ export class RestoreDialogWrapper extends DialogWrapper {
|
|||
service.post(
|
||||
baseUrl,
|
||||
this.view.model.toJSON()
|
||||
).then(function () {
|
||||
dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
|
||||
dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
|
||||
).then(function (res) {
|
||||
if (res.data.success) {
|
||||
dialogWrapper.alertify.success(gettext('Restore job created.'), 5);
|
||||
dialogWrapper.pgBrowser.Events.trigger('pgadmin-bgprocess:created', dialogWrapper);
|
||||
} else {
|
||||
dialogWrapper.alertify.alert(
|
||||
gettext('Restore job creation failed.'),
|
||||
res.data.errormsg
|
||||
);
|
||||
}
|
||||
}).catch(function (error) {
|
||||
try {
|
||||
const err = error.response.data;
|
||||
|
|
|
@ -17,7 +17,7 @@ import simplejson as json
|
|||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
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
|
||||
|
||||
|
||||
|
@ -62,13 +62,22 @@ class RestoreJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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):
|
||||
url = self.backup_options['url'].format(self.server_id)
|
||||
job_id = backup_utils.create_backup_job(self.tester, url,
|
||||
|
|
|
@ -9,10 +9,11 @@
|
|||
|
||||
import sys
|
||||
import simplejson as json
|
||||
import os
|
||||
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
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 \
|
||||
database_utils
|
||||
|
||||
|
@ -291,13 +292,22 @@ class RestoreCreateJobTest(BaseTestGenerator):
|
|||
]
|
||||
|
||||
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(
|
||||
"default_binary_paths is not set for the server {0}".format(
|
||||
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.current_user')
|
||||
@patch('pgadmin.tools.restore.RestoreMessage')
|
||||
|
|
|
@ -284,6 +284,18 @@ def get_complete_file_path(file):
|
|||
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
|
||||
ACCESSKEY_FIELDS = [
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
||||
import {TreeFake} from '../tree/tree_fake';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios/index';
|
||||
|
||||
const context = describe;
|
||||
|
||||
|
@ -93,7 +95,9 @@ describe('BackupDialog', () => {
|
|||
});
|
||||
|
||||
describe('#draw', () => {
|
||||
let networkMock;
|
||||
beforeEach(() => {
|
||||
networkMock = new MockAdapter(axios);
|
||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||
alertifySpy['backup_objects'] = jasmine.createSpy('backup_objects');
|
||||
backupDialog = new BackupDialog(
|
||||
|
@ -106,6 +110,10 @@ describe('BackupDialog', () => {
|
|||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
networkMock.restore();
|
||||
});
|
||||
|
||||
context('there are no ancestors of the type server', () => {
|
||||
it('does not create a dialog', () => {
|
||||
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
||||
|
@ -183,19 +191,27 @@ describe('BackupDialog', () => {
|
|||
alertifySpy['backup_objects'].and
|
||||
.returnValue(backupDialogResizeToSpy);
|
||||
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);
|
||||
expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
|
||||
expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy['backup_objects']).toHaveBeenCalledWith(true);
|
||||
expect(backupDialogResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
|
||||
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Backup Error',
|
||||
'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) => {
|
||||
dataSentToServer = request.data;
|
||||
return [200, {}];
|
||||
return [200, {'success': 1}];
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -485,7 +485,7 @@ describe('BackupDialogWrapper', () => {
|
|||
|
||||
networkMock.onPost('/backup/job/10/object').reply((request) => {
|
||||
dataSentToServer = request.data;
|
||||
return [200, {}];
|
||||
return [200, {'success': 1}];
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
import {BackupDialog} from '../../../pgadmin/tools/backup/static/js/backup_dialog';
|
||||
import {TreeFake} from '../tree/tree_fake';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios/index';
|
||||
|
||||
const context = describe;
|
||||
|
||||
|
@ -48,7 +50,9 @@ describe('GlobalServerBackupDialog', () => {
|
|||
});
|
||||
|
||||
describe('#draw', () => {
|
||||
let networkMock;
|
||||
beforeEach(() => {
|
||||
networkMock = new MockAdapter(axios);
|
||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||
alertifySpy['BackupDialog_globals'] = jasmine.createSpy('BackupDialog_globals');
|
||||
alertifySpy['BackupDialog_server'] = jasmine.createSpy('BackupDialog_server');
|
||||
|
@ -62,6 +66,10 @@ describe('GlobalServerBackupDialog', () => {
|
|||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
networkMock.restore();
|
||||
});
|
||||
|
||||
context('there are no ancestors of the type server', () => {
|
||||
it('does not create a dialog', () => {
|
||||
pgBrowser.treeMenu.selectNode([{id: 'level1'}]);
|
||||
|
@ -144,21 +152,29 @@ describe('GlobalServerBackupDialog', () => {
|
|||
alertifySpy['BackupDialog_server'].and
|
||||
.returnValue(serverResizeToSpy);
|
||||
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', () => {
|
||||
it('displays the dialog', () => {
|
||||
it('displays the dialog', (done) => {
|
||||
backupDialog.draw(null, [serverTreeNode], {globals: true});
|
||||
expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
|
||||
expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy['BackupDialog_globals']).toHaveBeenCalledWith(true);
|
||||
expect(globalResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
context('dialog for server backup', () => {
|
||||
it('displays the dialog', () => {
|
||||
it('displays the dialog', (done) => {
|
||||
backupDialog.draw(null, [serverTreeNode], {server: true});
|
||||
expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
|
||||
expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy['BackupDialog_server']).toHaveBeenCalledWith(true);
|
||||
expect(serverResizeToSpy.resizeTo).toHaveBeenCalledWith('60%', '50%');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
//////////////////////////////////////////////////////////////
|
||||
import {TreeFake} from '../tree/tree_fake';
|
||||
import {RestoreDialog} from '../../../pgadmin/tools/restore/static/js/restore_dialog';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from 'axios/index';
|
||||
|
||||
const context = describe;
|
||||
|
||||
|
@ -81,7 +83,9 @@ describe('RestoreDialog', () => {
|
|||
});
|
||||
|
||||
describe('#draw', () => {
|
||||
let networkMock;
|
||||
beforeEach(() => {
|
||||
networkMock = new MockAdapter(axios);
|
||||
alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']);
|
||||
alertifySpy['pg_restore'] = jasmine.createSpy('pg_restore');
|
||||
restoreDialog = new RestoreDialog(
|
||||
|
@ -94,6 +98,10 @@ describe('RestoreDialog', () => {
|
|||
pgBrowser.get_preference = jasmine.createSpy('get_preferences');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
networkMock.restore();
|
||||
});
|
||||
|
||||
context('there are no ancestors of the type server', () => {
|
||||
it('does not create a dialog', () => {
|
||||
pgBrowser.treeMenu.selectNode([{id: 'root'}]);
|
||||
|
@ -172,28 +180,36 @@ describe('RestoreDialog', () => {
|
|||
.returnValue(spy);
|
||||
pgBrowser.get_preference.and.returnValue({value: '/some/path'});
|
||||
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});
|
||||
expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
|
||||
'Restore (some-server-label: some-tree-label)',
|
||||
[{id: 'serverTreeNode'}],
|
||||
{
|
||||
_id: 10,
|
||||
_type: 'server',
|
||||
label: 'some-tree-label',
|
||||
},
|
||||
pgBrowser.Nodes.server
|
||||
);
|
||||
expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy['pg_restore']).toHaveBeenCalledWith(
|
||||
'Restore (some-server-label: some-tree-label)',
|
||||
[{id: 'serverTreeNode'}],
|
||||
{
|
||||
_id: 10,
|
||||
_type: 'server',
|
||||
label: 'some-tree-label',
|
||||
},
|
||||
pgBrowser.Nodes.server
|
||||
);
|
||||
expect(spy.resizeTo).toHaveBeenCalledWith('65%', '60%');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
|
||||
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
||||
setTimeout(() => {
|
||||
expect(alertifySpy.alert).toHaveBeenCalledWith('Restore Error',
|
||||
'Databases with = symbols in the name cannot be backed up or restored using this utility.');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -397,7 +397,7 @@ describe('RestoreDialogWrapper', () => {
|
|||
beforeEach(() => {
|
||||
networkMock.onPost('/restore/job/10').reply((request) => {
|
||||
dataSentToServer = request.data;
|
||||
return [200, {}];
|
||||
return [200, {'success': 1}];
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -436,6 +436,9 @@ def create_server(server):
|
|||
server_id = cur.lastrowid
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
type = get_server_type(server)
|
||||
server['type'] = type
|
||||
# Add server info to parent_node_dict
|
||||
regression.parent_node_dict["server"].append(
|
||||
{
|
||||
|
@ -899,3 +902,35 @@ def create_schema(server, db_name, schema_name):
|
|||
|
||||
except Exception:
|
||||
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)
|
||||
|
|