Add a (configurable) limit to the number of pgAgent job history rows displayed on the statistics tab. Fixes #3072

This commit is contained in:
Murtuza Zabuawala 2018-04-06 10:00:25 +01:00 committed by Dave Page
parent fa1854bd85
commit 8ec51412c3
16 changed files with 788 additions and 273 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -45,6 +45,8 @@ Use fields on the *Properties* panel to specify browser properties:
* Include a value in the *Count rows if estimated less than* field to perform a SELECT count(*) if the estimated number of rows in a table (as read from the table statistics) is below the specified limit. After performing the SELECT count(*), pgAdmin will display the row count. The default is 2000.
* Provide a value in the *Maximum job history rows* field to limit the number of rows to show on the statistics tab for pgAgent jobs. The default is 250.
**The Dashboards Node**
Expand the *Dashboards* node to specify your dashboard display preferences.

View File

@ -88,6 +88,7 @@ Bug fixes
| `Bug #3060 <https://redmine.postgresql.org/issues/3060>`_ - Fix quoting of function names in RE-SQL
| `Bug #3066 <https://redmine.postgresql.org/issues/3066>`_ - Ensure column names on indexes on views are properly quoted in RE-SQL
| `Bug #3067 <https://redmine.postgresql.org/issues/3067>`_ - Prevent the filter dialog CodeMirror from overflowing onto the button bar of the dialog
| `Bug #3072 <https://redmine.postgresql.org/issues/3072>`_ - Add a (configurable) limit to the number of pgAgent job history rows displayed on the statistics tab
| `Bug #3073 <https://redmine.postgresql.org/issues/3073>`_ - Ensure the pgAgent job start/end time grid fields synchronise with the subnode control and validate correctly
| `Bug #3075 <https://redmine.postgresql.org/issues/3075>`_ - Runtime issue causing Select, Update, and Insert script generation for a table fails to load
| `Bug #3077 <https://redmine.postgresql.org/issues/3077>`_ - Remove dependency on standards_conforming_strings being enabled

View File

@ -37,6 +37,8 @@ from pgadmin.settings import get_setting
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import make_json_response
from pgadmin.utils.preferences import Preferences
from pgadmin.browser.register_browser_preferences import \
register_browser_preferences
try:
import urllib.request as urlreq
@ -211,275 +213,7 @@ class BrowserModule(PgAdminModule):
return scripts
def register_preferences(self):
self.show_system_objects = self.preference.register(
'display', 'show_system_objects',
gettext("Show system objects?"), 'boolean', False,
category_label=gettext('Display')
)
self.preference.register(
'display', 'enable_acitree_animation',
gettext("Enable browser tree animation?"), 'boolean', True,
category_label=gettext('Display')
)
self.preference.register(
'display', 'enable_alertify_animation',
gettext("Enable dialogue/notification animation?"), 'boolean',
True, category_label=gettext('Display')
)
self.table_row_count_threshold = self.preference.register(
'properties', 'table_row_count_threshold',
gettext("Count rows if estimated less than"), 'integer', 2000,
category_label=gettext('Properties')
)
fields = [
{'name': 'key', 'type': 'keyCode', 'label': gettext('Key')},
{'name': 'shift', 'type': 'checkbox', 'label': gettext('Shift')},
{'name': 'control', 'type': 'checkbox', 'label': gettext('Ctrl')},
{'name': 'alt', 'type': 'checkbox', 'label': gettext('Alt/Option')}
]
self.preference.register(
'keyboard_shortcuts',
'browser_tree',
gettext('Browser tree'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 66, 'char': 'b'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'tabbed_panel_backward',
gettext('Tabbed panel backward'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 91, 'char': '['}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'tabbed_panel_forward',
gettext('Tabbed panel forward'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 93, 'char': ']'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_file',
gettext('File main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 70, 'char': 'f'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_object',
gettext('Object main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 79, 'char': 'o'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_tools',
gettext('Tools main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 76, 'char': 'l'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_help',
gettext('Help main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 72, 'char': 'h'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_query_tool',
gettext('Open query tool'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 81, 'char': 'q'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_view_data',
gettext('View data'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 86, 'char': 'v'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_create',
gettext('Create object'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 78, 'char': 'n'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_properties',
gettext('Edit object properties'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 69, 'char': 'e'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_delete',
gettext('Delete object'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 68, 'char': 'd'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'context_menu',
gettext('Open context menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 67, 'char': 'c'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'direct_debugging',
gettext('Direct debugging'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 71, 'char': 'g'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_forward',
gettext('Dialog tab forward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 93, 'char': ']'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_backward',
gettext('Dialog tab backward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 91, 'char': '['}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
register_browser_preferences(self)
def get_exposed_url_endpoints(self):
"""

View File

@ -0,0 +1,293 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from flask_babelex import gettext
def register_browser_preferences(self):
self.show_system_objects = self.preference.register(
'display', 'show_system_objects',
gettext("Show system objects?"), 'boolean', False,
category_label=gettext('Display')
)
self.preference.register(
'display', 'enable_acitree_animation',
gettext("Enable browser tree animation?"), 'boolean', True,
category_label=gettext('Display')
)
self.preference.register(
'display', 'enable_alertify_animation',
gettext("Enable dialogue/notification animation?"), 'boolean',
True, category_label=gettext('Display')
)
self.table_row_count_threshold = self.preference.register(
'properties', 'table_row_count_threshold',
gettext("Count rows if estimated less than"), 'integer', 2000,
category_label=gettext('Properties')
)
self.pg_agent_row_threshold = self.preference.register(
'properties', 'pgagent_row_threshold',
gettext("Maximum job history rows"), 'integer', 250,
category_label=gettext('Properties'),
min_val=1, max_val=9999,
help_str=gettext(
'The maximum number of history rows to show on '
'the Statistics tab for pgAgent jobs'
)
)
fields = [
{'name': 'key', 'type': 'keyCode', 'label': gettext('Key')},
{'name': 'shift', 'type': 'checkbox', 'label': gettext('Shift')},
{'name': 'control', 'type': 'checkbox', 'label': gettext('Ctrl')},
{'name': 'alt', 'type': 'checkbox', 'label': gettext('Alt/Option')}
]
self.preference.register(
'keyboard_shortcuts',
'browser_tree',
gettext('Browser tree'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 66, 'char': 'b'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'tabbed_panel_backward',
gettext('Tabbed panel backward'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 91, 'char': '['}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'tabbed_panel_forward',
gettext('Tabbed panel forward'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 93, 'char': ']'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_file',
gettext('File main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 70, 'char': 'f'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_object',
gettext('Object main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 79, 'char': 'o'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_tools',
gettext('Tools main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 76, 'char': 'l'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'main_menu_help',
gettext('Help main menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 72, 'char': 'h'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_query_tool',
gettext('Open query tool'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 81, 'char': 'q'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_view_data',
gettext('View data'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 86, 'char': 'v'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_create',
gettext('Create object'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 78, 'char': 'n'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_properties',
gettext('Edit object properties'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 69, 'char': 'e'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'sub_menu_delete',
gettext('Delete object'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 68, 'char': 'd'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'context_menu',
gettext('Open context menu'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 67, 'char': 'c'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'direct_debugging',
gettext('Direct debugging'),
'keyboardshortcut',
{
'alt': True,
'shift': True,
'control': False,
'key': {'key_code': 71, 'char': 'g'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_forward',
gettext('Dialog tab forward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 93, 'char': ']'}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)
self.preference.register(
'keyboard_shortcuts',
'dialog_tab_backward',
gettext('Dialog tab backward'),
'keyboardshortcut',
{
'alt': False,
'shift': True,
'control': True,
'key': {'key_code': 91, 'char': '['}
},
category_label=gettext('Keyboard shortcuts'),
fields=fields
)

View File

@ -22,6 +22,7 @@ from pgadmin.browser.server_groups import servers
from pgadmin.utils.ajax import make_json_response, internal_server_error, \
make_response as ajax_response, gone, success_return
from pgadmin.utils.driver import get_driver
from pgadmin.utils.preferences import Preferences
class JobModule(CollectionNodeModule):
@ -415,10 +416,16 @@ SELECT EXISTS(
otherwise it will return statistics for all the databases in that
server.
"""
pref = Preferences.module('browser')
rows_threshold = pref.preference(
'pgagent_row_threshold'
)
status, res = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'stats.sql']),
jid=jid, conn=self.conn
jid=jid, conn=self.conn,
rows_threshold=rows_threshold.get()
)
)

View File

@ -19,6 +19,7 @@ from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, gone, \
make_response as ajax_response, internal_server_error
from pgadmin.utils.driver import get_driver
from pgadmin.utils.preferences import Preferences
from config import PG_DEFAULT_DRIVER
@ -570,10 +571,16 @@ SELECT EXISTS(
otherwise it will return statistics for all the databases in that
server.
"""
pref = Preferences.module('browser')
rows_threshold = pref.preference(
'pgagent_row_threshold'
)
status, res = self.conn.execute_dict(
render_template(
"/".join([self.template_path, 'stats.sql']),
jid=jid, jstid=jstid, conn=self.conn
jid=jid, jstid=jstid, conn=self.conn,
rows_threshold=rows_threshold.get()
)
)

View File

@ -8,4 +8,5 @@ FROM
pgagent.pga_joblog
WHERE
jlgjobid = {{ jid|qtLiteral }}::integer
ORDER BY jlgid DESC;
ORDER BY jlgid DESC
LIMIT {{ rows_threshold }};

View File

@ -10,4 +10,5 @@ FROM
pgagent.pga_jobsteplog
WHERE
jsljstid = {{ jstid|qtLiteral }}::integer
ORDER BY jslid DESC;
ORDER BY jslid DESC
LIMIT {{ rows_threshold }};

View File

@ -0,0 +1,15 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
class PgAgentCreateTestCase(BaseTestGenerator):
def runTest(self):
return

View File

@ -0,0 +1,89 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import simplejson as json
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as pgagent_utils
class PgAgentAddTestCase(BaseTestGenerator):
"""This class will test the add pgAgent job API"""
scenarios = [
('Add pgAgent job', dict(url='/browser/pga_job/obj/'))
]
def setUp(self):
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
if not flag:
self.skipTest(msg)
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
if not flag:
self.skipTest(msg)
def runTest(self):
"""This function will adds pgAgent job"""
self.pgagent_job = "test_job_add%s" % str(uuid.uuid4())[1:8]
data = {
'jobname': self.pgagent_job,
'jobenabled': True,
'jobhostagent': '',
'jobjclid': 1,
'jobdesc': '',
'jsteps': [{
'jstid': None,
'jstjobid': None,
'jstname': 'test_step',
'jstdesc': '',
'jstenabled': True,
'jstkind': True,
'jstconntype': True,
'jstcode': 'SELECT 1;',
'jstconnstr': None,
'jstdbname': 'postgres',
'jstonerror': 'f',
'jstnextrun': '',
}],
'jschedules': [{
'jscid': None,
'jscjobid': None,
'jscname': 'test_sch',
'jscdesc': '',
'jscenabled': True,
'jscstart': '2050-01-01 12:14:21 +05:30',
'jscend': None,
'jscweekdays': [False] * 7,
'jscmonthdays': [False] * 32,
'jscmonths': [False] * 12,
'jschours': [False] * 24,
'jscminutes': [False] * 60,
'jscexceptions': [],
}],
}
response = self.tester.post(
'{0}{1}/{2}/'.format(
self.url, str(utils.SERVER_GROUP), str(self.server_id)
),
data=json.dumps(data),
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
response_data = json.loads(response.data)
self.job_id = response_data['node']['_id']
is_present = pgagent_utils.verify_pgagent_job(self)
self.assertTrue(
is_present, "pgAgent job was not created successfully"
)
def tearDown(self):
"""Clean up code"""
pgagent_utils.delete_pgagent_job(self)

View File

@ -0,0 +1,49 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as pgagent_utils
class PgAgentDeleteTestCase(BaseTestGenerator):
"""This class will test the delete pgAgent job API"""
scenarios = [
('Delete pgAgent job', dict(url='/browser/pga_job/obj/'))
]
def setUp(self):
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
if not flag:
self.skipTest(msg)
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
if not flag:
self.skipTest(msg)
name = "test_job_delete%s" % str(uuid.uuid4())[1:8]
self.job_id = pgagent_utils.create_pgagent_job(self, name)
def runTest(self):
"""This function will deletes pgAgent job"""
response = self.tester.delete(
'{0}{1}/{2}/{3}'.format(
self.url, str(utils.SERVER_GROUP), str(self.server_id),
str(self.job_id)
),
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
is_present = pgagent_utils.verify_pgagent_job(self)
self.assertFalse(
is_present, "pgAgent job was not deleted successfully"
)
def tearDown(self):
"""Clean up code"""
pgagent_utils.delete_pgagent_job(self)

View File

@ -0,0 +1,51 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import simplejson as json
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as pgagent_utils
class PgAgentPutTestCase(BaseTestGenerator):
"""This class will test the put pgAgent job API"""
scenarios = [
('Put pgAgent job', dict(url='/browser/pga_job/obj/'))
]
def setUp(self):
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
if not flag:
self.skipTest(msg)
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
if not flag:
self.skipTest(msg)
name = "test_job_put%s" % str(uuid.uuid4())[1:8]
self.job_id = pgagent_utils.create_pgagent_job(self, name)
def runTest(self):
"""This function will put pgAgent job"""
data = {
"jobdesc": "This is a test comment",
}
response = self.tester.put(
'{0}{1}/{2}/{3}'.format(
self.url, str(utils.SERVER_GROUP), str(self.server_id),
str(self.job_id)
),
data=json.dumps(data),
follow_redirects=True,
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
def tearDown(self):
"""Clean up code"""
pgagent_utils.delete_pgagent_job(self)

View File

@ -0,0 +1,45 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as pgagent_utils
class PgAgentGetTestCase(BaseTestGenerator):
"""This class will test the get pgAgent job API"""
scenarios = [
('Get pgAgent job', dict(url='/browser/pga_job/obj/'))
]
def setUp(self):
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
if not flag:
self.skipTest(msg)
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
if not flag:
self.skipTest(msg)
name = "test_job_get%s" % str(uuid.uuid4())[1:8]
self.job_id = pgagent_utils.create_pgagent_job(self, name)
def runTest(self):
"""This function will get pgAgent job"""
response = self.tester.get(
'{0}{1}/{2}/{3}'.format(
self.url, str(utils.SERVER_GROUP), str(self.server_id),
str(self.job_id)
),
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
def tearDown(self):
"""Clean up code"""
pgagent_utils.delete_pgagent_job(self)

View File

@ -0,0 +1,45 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as pgagent_utils
class PgAgentStatsTestCase(BaseTestGenerator):
"""This class will test the stats pgAgent job API"""
scenarios = [
('Check the stats of pgAgent job', dict(url='/browser/pga_job/stats/'))
]
def setUp(self):
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
if not flag:
self.skipTest(msg)
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
if not flag:
self.skipTest(msg)
name = "test_job_get%s" % str(uuid.uuid4())[1:8]
self.job_id = pgagent_utils.create_pgagent_job(self, name)
def runTest(self):
"""This function will check stats of pgAgent job"""
response = self.tester.get(
'{0}{1}/{2}/{3}'.format(
self.url, str(utils.SERVER_GROUP), str(self.server_id),
str(self.job_id)
),
content_type='html/json'
)
self.assertEquals(response.status_code, 200)
def tearDown(self):
"""Clean up code"""
pgagent_utils.delete_pgagent_job(self)

View File

@ -0,0 +1,175 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from __future__ import print_function
import sys
import traceback
from regression.python_test_utils import test_utils as utils
from regression import parent_node_dict
from pgadmin.utils import server_utils as server_utils
def is_valid_server_to_run_pgagent(self):
"""
This function checks if server is valid for the pgAgent job.
"""
self.server_id = parent_node_dict["server"][-1]["server_id"]
server_con = server_utils.connect_server(self, self.server_id)
if not server_con["info"] == "Server connected.":
raise Exception("Could not connect to server to add pgAgent job.")
if "type" in server_con["data"]:
if server_con["data"]["type"] == "gpdb":
message = "pgAgent is not supported by Greenplum."
return False, message
return True, None
def is_pgagent_installed_on_server(self):
"""
This function checks if the pgAgent is installed properly.
"""
try:
connection = utils.get_db_connection(
self.server['db'],
self.server['username'],
self.server['db_password'],
self.server['host'],
self.server['port'],
self.server['sslmode']
)
pg_cursor = connection.cursor()
SQL = """
SELECT
has_table_privilege(
'pgagent.pga_job', 'INSERT, SELECT, UPDATE'
) has_priviledge
WHERE EXISTS(
SELECT has_schema_privilege('pgagent', 'USAGE')
WHERE EXISTS(
SELECT cl.oid FROM pg_class cl
LEFT JOIN pg_namespace ns ON ns.oid=relnamespace
WHERE relname='pga_job' AND nspname='pgagent'
)
)
"""
pg_cursor.execute(SQL)
result = pg_cursor.fetchone()
if result is None:
connection.close()
message = "Make sure pgAgent is installed properly."
return False, message
SQL = """
SELECT EXISTS(
SELECT 1 FROM information_schema.columns
WHERE
table_schema='pgagent' AND table_name='pga_jobstep' AND
column_name='jstconnstr'
) has_connstr
"""
pg_cursor.execute(SQL)
result = pg_cursor.fetchone()
if result is None:
connection.close()
message = "Make sure pgAgent is installed properly."
return False, message
connection.close()
return True, None
except Exception:
traceback.print_exc(file=sys.stderr)
def create_pgagent_job(self, name):
"""
This function create the pgAgent job.
"""
try:
connection = utils.get_db_connection(
self.server['db'],
self.server['username'],
self.server['db_password'],
self.server['host'],
self.server['port'],
self.server['sslmode']
)
old_isolation_level = connection.isolation_level
connection.set_isolation_level(0)
pg_cursor = connection.cursor()
pg_cursor.execute(
"""
INSERT INTO pgagent.pga_job(
jobjclid, jobname, jobdesc, jobhostagent, jobenabled
) VALUES (
1::integer, '{0}'::text, ''::text, ''::text, true
) RETURNING jobid;
""".format(name)
)
job_id = pg_cursor.fetchone()
connection.set_isolation_level(old_isolation_level)
connection.commit()
connection.close()
return job_id[0]
except Exception:
traceback.print_exc(file=sys.stderr)
def delete_pgagent_job(self):
"""
This function deletes the pgAgent job.
"""
try:
connection = utils.get_db_connection(
self.server['db'],
self.server['username'],
self.server['db_password'],
self.server['host'],
self.server['port'],
self.server['sslmode']
)
old_isolation_level = connection.isolation_level
connection.set_isolation_level(0)
pg_cursor = connection.cursor()
pg_cursor.execute(
"DELETE FROM pgagent.pga_job "
"WHERE jobid = '%s'::integer;" % self.job_id
)
connection.set_isolation_level(old_isolation_level)
connection.commit()
connection.close()
except Exception:
traceback.print_exc(file=sys.stderr)
def verify_pgagent_job(self):
"""
This function deletes the pgAgent job.
"""
try:
connection = utils.get_db_connection(
self.server['db'],
self.server['username'],
self.server['db_password'],
self.server['host'],
self.server['port'],
self.server['sslmode']
)
pg_cursor = connection.cursor()
pg_cursor.execute(
"SELECT COUNT(*) FROM pgagent.pga_job "
"WHERE jobid = '%s'::integer;" % self.job_id
)
result = pg_cursor.fetchone()
count = result[0]
connection.close()
return count is not None and int(count) != 0
except Exception:
traceback.print_exc(file=sys.stderr)