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

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)