mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Add support for Resource Groups in EDB Advanced Server 9.4+.
This commit is contained in:
@@ -0,0 +1,565 @@
|
|||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# pgAdmin 4 - PostgreSQL Tools
|
||||||
|
#
|
||||||
|
# Copyright (C) 2013 - 2016, The pgAdmin Development Team
|
||||||
|
# This software is released under the PostgreSQL Licence
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
"""Implements Resource Groups for PPAS 9.4 and above"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from flask import render_template, make_response, request, jsonify
|
||||||
|
from flask.ext.babel import gettext
|
||||||
|
from pgadmin.utils.ajax import make_json_response, \
|
||||||
|
make_response as ajax_response, internal_server_error
|
||||||
|
from pgadmin.browser.utils import NodeView
|
||||||
|
from pgadmin.browser.collection import CollectionNodeModule
|
||||||
|
import pgadmin.browser.server_groups.servers as servers
|
||||||
|
from pgadmin.utils.ajax import precondition_required
|
||||||
|
from pgadmin.utils.driver import get_driver
|
||||||
|
from config import PG_DEFAULT_DRIVER
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceGroupModule(CollectionNodeModule):
|
||||||
|
"""
|
||||||
|
class ResourceGroupModule(CollectionNodeModule)
|
||||||
|
|
||||||
|
A module class for Resource Group node derived from CollectionNodeModule.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(*args, **kwargs)
|
||||||
|
- Method is used to initialize the ResourceGroupModule and it's base module.
|
||||||
|
|
||||||
|
* BackendSupported(manager, **kwargs)
|
||||||
|
- This function is used to check the database server type and version.
|
||||||
|
Resource Group only supported in PPAS 9.4 and above.
|
||||||
|
|
||||||
|
* get_nodes(gid, sid, did)
|
||||||
|
- Method is used to generate the browser collection node.
|
||||||
|
|
||||||
|
* node_inode()
|
||||||
|
- Method is overridden from its base class to make the node as leaf node.
|
||||||
|
|
||||||
|
* script_load()
|
||||||
|
- Load the module script for resource group, when any of the server node is
|
||||||
|
initialized.
|
||||||
|
"""
|
||||||
|
|
||||||
|
NODE_TYPE = 'resource_group'
|
||||||
|
COLLECTION_LABEL = gettext("Resource Groups")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Method is used to initialize the ResourceGroupModule and it's base module.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*args:
|
||||||
|
**kwargs:
|
||||||
|
"""
|
||||||
|
self.min_ver = None
|
||||||
|
self.max_ver = None
|
||||||
|
|
||||||
|
super(ResourceGroupModule, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def BackendSupported(self, manager, **kwargs):
|
||||||
|
"""
|
||||||
|
This function is used to check the database server type and version.
|
||||||
|
Resource Group only supported in PPAS 9.4 and above.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
manager: Object of the server manager class.
|
||||||
|
**kwargs:
|
||||||
|
|
||||||
|
Returns: True or False
|
||||||
|
"""
|
||||||
|
if super(ResourceGroupModule, self).BackendSupported(manager, **kwargs):
|
||||||
|
conn = manager.connection()
|
||||||
|
# If DB is not connected then return error to browser
|
||||||
|
if not conn.connected():
|
||||||
|
return precondition_required(gettext("Connection to the server has been lost!"))
|
||||||
|
if manager.server_type == 'ppas' and manager.version >= 90400:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_nodes(self, gid, sid):
|
||||||
|
"""
|
||||||
|
Method is used to generate the browser collection node
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
"""
|
||||||
|
yield self.generate_browser_collection_node(sid)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def node_inode(self):
|
||||||
|
"""
|
||||||
|
Override this property to make the node as leaf node.
|
||||||
|
|
||||||
|
Returns: False as this is the leaf node
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def script_load(self):
|
||||||
|
"""
|
||||||
|
Load the module script for resource group, when any of the server node is initialized.
|
||||||
|
|
||||||
|
Returns: node type of the server module.
|
||||||
|
"""
|
||||||
|
return servers.ServerModule.NODE_TYPE
|
||||||
|
|
||||||
|
blueprint = ResourceGroupModule(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ResourceGroupView(NodeView):
|
||||||
|
"""
|
||||||
|
class ResourceGroupView(NodeView)
|
||||||
|
|
||||||
|
A view class for resource group node derived from NodeView. This class is
|
||||||
|
responsible for all the stuff related to view like create/update/delete resource group,
|
||||||
|
showing properties of resource group node, showing sql in sql pane.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
-------
|
||||||
|
* __init__(**kwargs)
|
||||||
|
- Method is used to initialize the ResourceGroupView and it's base view.
|
||||||
|
|
||||||
|
* module_js()
|
||||||
|
- This property defines (if javascript) exists for this node.
|
||||||
|
Override this property for your own logic
|
||||||
|
|
||||||
|
* check_precondition()
|
||||||
|
- This function will behave as a decorator which will checks
|
||||||
|
database connection before running view, it will also attaches
|
||||||
|
manager,conn & template_path properties to self
|
||||||
|
|
||||||
|
* list()
|
||||||
|
- This function is used to list all the resource group nodes within that collection.
|
||||||
|
|
||||||
|
* nodes()
|
||||||
|
- This function will used to create all the child node within that collection.
|
||||||
|
Here it will create all the resource group node.
|
||||||
|
|
||||||
|
* properties(gid, sid, did, rg_id)
|
||||||
|
- This function will show the properties of the selected resource group node
|
||||||
|
|
||||||
|
* create(gid, sid, did, rg_id)
|
||||||
|
- This function will create the new resource group object
|
||||||
|
|
||||||
|
* update(gid, sid, did, rg_id)
|
||||||
|
- This function will update the data for the selected resource group node
|
||||||
|
|
||||||
|
* delete(self, gid, sid, rg_id):
|
||||||
|
- This function will drop the resource group object
|
||||||
|
|
||||||
|
* msql(gid, sid, did, rg_id)
|
||||||
|
- This function is used to return modified SQL for the selected resource group node
|
||||||
|
|
||||||
|
* get_sql(data, rg_id)
|
||||||
|
- This function will generate sql from model data
|
||||||
|
|
||||||
|
* sql(gid, sid, did, rg_id):
|
||||||
|
- This function will generate sql to show it in sql pane for the selected resource group node.
|
||||||
|
"""
|
||||||
|
|
||||||
|
node_type = blueprint.node_type
|
||||||
|
|
||||||
|
parent_ids = [
|
||||||
|
{'type': 'int', 'id': 'gid'},
|
||||||
|
{'type': 'int', 'id': 'sid'}
|
||||||
|
]
|
||||||
|
ids = [
|
||||||
|
{'type': 'int', 'id': 'rg_id'}
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = dict({
|
||||||
|
'obj': [
|
||||||
|
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||||
|
{'get': 'list', 'post': 'create'}
|
||||||
|
],
|
||||||
|
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||||
|
'sql': [{'get': 'sql'}],
|
||||||
|
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||||
|
'stats': [{'get': 'statistics'}],
|
||||||
|
'dependency': [{'get': 'dependencies'}],
|
||||||
|
'dependent': [{'get': 'dependents'}],
|
||||||
|
'module.js': [{}, {}, {'get': 'module_js'}]
|
||||||
|
})
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
"""
|
||||||
|
Method is used to initialize the ResourceGroupView and it's base view.
|
||||||
|
Also initialize all the variables create/used dynamically like conn, template_path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**kwargs:
|
||||||
|
"""
|
||||||
|
self.conn = None
|
||||||
|
self.template_path = None
|
||||||
|
|
||||||
|
super(ResourceGroupView, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
def module_js(self):
|
||||||
|
"""
|
||||||
|
This property defines (if javascript) exists for this node.
|
||||||
|
Override this property for your own logic.
|
||||||
|
"""
|
||||||
|
return make_response(
|
||||||
|
render_template(
|
||||||
|
"resource_groups/js/resource_groups.js",
|
||||||
|
_=gettext
|
||||||
|
),
|
||||||
|
200, {'Content-Type': 'application/x-javascript'}
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_precondition(f):
|
||||||
|
"""
|
||||||
|
This function will behave as a decorator which will checks
|
||||||
|
database connection before running view, it will also attaches
|
||||||
|
manager,conn & template_path properties to self
|
||||||
|
"""
|
||||||
|
@wraps(f)
|
||||||
|
def wrap(*args, **kwargs):
|
||||||
|
# Here args[0] will hold self & kwargs will hold gid,sid,did
|
||||||
|
self = args[0]
|
||||||
|
self.driver = get_driver(PG_DEFAULT_DRIVER)
|
||||||
|
self.manager = self.driver.connection_manager(kwargs['sid'])
|
||||||
|
self.conn = self.manager.connection()
|
||||||
|
|
||||||
|
# If DB not connected then return error to browser
|
||||||
|
if not self.conn.connected():
|
||||||
|
return precondition_required(
|
||||||
|
gettext(
|
||||||
|
"Connection to the server has been lost!"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.template_path = 'resource_groups/sql'
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return wrap
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def list(self, gid, sid):
|
||||||
|
"""
|
||||||
|
This function is used to list all the resource group nodes within that collection.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
"""
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']))
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
return ajax_response(
|
||||||
|
response=res['rows'],
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def nodes(self, gid, sid):
|
||||||
|
"""
|
||||||
|
This function will used to create all the child node within that collection.
|
||||||
|
Here it will create all the resource group node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
"""
|
||||||
|
res = []
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']))
|
||||||
|
status, result = self.conn.execute_2darray(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=result)
|
||||||
|
|
||||||
|
for row in result['rows']:
|
||||||
|
res.append(
|
||||||
|
self.blueprint.generate_browser_node(
|
||||||
|
row['oid'],
|
||||||
|
sid,
|
||||||
|
row['name'],
|
||||||
|
icon="icon-resource_group"
|
||||||
|
))
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
data=res,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def properties(self, gid, sid, rg_id):
|
||||||
|
"""
|
||||||
|
This function will show the properties of the selected resource group node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
rg_id: Resource Group ID
|
||||||
|
"""
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']), rgid=rg_id)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return ajax_response(
|
||||||
|
response=res['rows'][0],
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def create(self, gid, sid):
|
||||||
|
"""
|
||||||
|
This function will create the new resource group object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
"""
|
||||||
|
required_args = [
|
||||||
|
'name'
|
||||||
|
]
|
||||||
|
|
||||||
|
data = request.form if request.form else json.loads(request.data.decode())
|
||||||
|
for arg in required_args:
|
||||||
|
if arg not in data:
|
||||||
|
return make_json_response(
|
||||||
|
status=410,
|
||||||
|
success=0,
|
||||||
|
errormsg=gettext(
|
||||||
|
"Couldn't find the required parameter (%s)." % arg
|
||||||
|
)
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
# Below logic will create new resource group
|
||||||
|
sql = render_template("/".join([self.template_path, 'create.sql']), rgname=data['name'], conn=self.conn)
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
status, res = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
# Below logic will update the cpu_rate_limit and dirty_rate_limit for resource group
|
||||||
|
# we need to add this logic because in resource group you can't run multiple commands in one transaction.
|
||||||
|
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, conn=self.conn)
|
||||||
|
# Checking if we are not executing empty query
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
status, res = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
# Below logic is used to fetch the oid of the newly created resource group
|
||||||
|
sql = render_template("/".join([self.template_path, 'getoid.sql']), rgname=data['name'])
|
||||||
|
# Checking if we are not executing empty query
|
||||||
|
rg_id = 0
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
status, rg_id = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rg_id)
|
||||||
|
|
||||||
|
return jsonify(
|
||||||
|
node=self.blueprint.generate_browser_node(
|
||||||
|
rg_id,
|
||||||
|
sid,
|
||||||
|
data['name'],
|
||||||
|
icon="icon-resource_group"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def update(self, gid, sid, rg_id):
|
||||||
|
"""
|
||||||
|
This function will update the data for the selected resource group node
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
rg_id: Resource Group ID
|
||||||
|
"""
|
||||||
|
required_args = [
|
||||||
|
'name', 'cpu_rate_limit', 'dirty_rate_limit'
|
||||||
|
]
|
||||||
|
data = request.form if request.form else json.loads(request.data.decode())
|
||||||
|
|
||||||
|
try:
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']), rgid=rg_id)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
old_data = res['rows'][0]
|
||||||
|
for arg in required_args:
|
||||||
|
if arg not in data:
|
||||||
|
data[arg] = old_data[arg]
|
||||||
|
|
||||||
|
if data['name'] != old_data['name']:
|
||||||
|
sql = render_template("/".join([self.template_path, 'update.sql']),
|
||||||
|
oldname=old_data['name'], newname=data['name'], conn=self.conn)
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
status, res = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
# Below logic will update the cpu_rate_limit and dirty_rate_limit for resource group
|
||||||
|
# we need to add this logic because in resource group you can't run multiple commands
|
||||||
|
# in one transaction.
|
||||||
|
if (data['cpu_rate_limit'] != old_data['cpu_rate_limit']) \
|
||||||
|
or (data['dirty_rate_limit'] != old_data['dirty_rate_limit']):
|
||||||
|
sql = render_template("/".join([self.template_path, 'update.sql']), data=data, conn=self.conn)
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
status, res = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info="Resource Group updated",
|
||||||
|
data={
|
||||||
|
'id': rg_id,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def delete(self, gid, sid, rg_id):
|
||||||
|
"""
|
||||||
|
This function will drop the resource group object
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
rg_id: Resource Group ID
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get name for resource group from rg_id
|
||||||
|
sql = render_template("/".join([self.template_path, 'delete.sql']), rgid=rg_id, conn=self.conn)
|
||||||
|
status, rgname = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=rgname)
|
||||||
|
# drop resource group
|
||||||
|
sql = render_template("/".join([self.template_path, 'delete.sql']), rgname=rgname, conn=self.conn)
|
||||||
|
status, res = self.conn.execute_scalar(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
return make_json_response(
|
||||||
|
success=1,
|
||||||
|
info=gettext("Resource Group dropped"),
|
||||||
|
data={
|
||||||
|
'id': rg_id,
|
||||||
|
'sid': sid,
|
||||||
|
'gid': gid,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def msql(self, gid, sid, rg_id=None):
|
||||||
|
"""
|
||||||
|
This function is used to return modified SQL for the selected resource group node
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
rg_id: Resource Group ID
|
||||||
|
"""
|
||||||
|
data = dict()
|
||||||
|
for k, v in request.args.items():
|
||||||
|
try:
|
||||||
|
data[k] = json.loads(v)
|
||||||
|
except ValueError:
|
||||||
|
data[k] = v
|
||||||
|
|
||||||
|
sql = self.get_sql(data, rg_id)
|
||||||
|
if sql and sql.strip('\n') and sql.strip(' '):
|
||||||
|
return make_json_response(
|
||||||
|
data=sql,
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return make_json_response(
|
||||||
|
data='-- Modified SQL --',
|
||||||
|
status=200
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_sql(self, data, rg_id=None):
|
||||||
|
"""
|
||||||
|
This function will generate sql from model data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Contains the value of name, cpu_rate_limit, dirty_rate_limit
|
||||||
|
rg_id: Resource Group Id
|
||||||
|
"""
|
||||||
|
required_args = [
|
||||||
|
'name', 'cpu_rate_limit', 'dirty_rate_limit'
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
if rg_id is not None:
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']), rgid=rg_id)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
old_data = res['rows'][0]
|
||||||
|
for arg in required_args:
|
||||||
|
if arg not in data:
|
||||||
|
data[arg] = old_data[arg]
|
||||||
|
|
||||||
|
sql = ''
|
||||||
|
name_changed = False
|
||||||
|
if data['name'] != old_data['name']:
|
||||||
|
name_changed = True
|
||||||
|
sql = render_template("/".join([self.template_path, 'update.sql']),
|
||||||
|
oldname=old_data['name'], newname=data['name'], conn=self.conn)
|
||||||
|
if (data['cpu_rate_limit'] != old_data['cpu_rate_limit'])\
|
||||||
|
or data['dirty_rate_limit'] != old_data['dirty_rate_limit']:
|
||||||
|
if name_changed:
|
||||||
|
sql += "\n-- Following query will be executed in a separate transaction\n"
|
||||||
|
sql += render_template("/".join([self.template_path, 'update.sql']), data=data, conn=self.conn)
|
||||||
|
else:
|
||||||
|
sql = render_template("/".join([self.template_path, 'create.sql']), rgname=data['name'], conn=self.conn)
|
||||||
|
if ('cpu_rate_limit' in data and data['cpu_rate_limit'] > 0) \
|
||||||
|
or ('dirty_rate_limit' in data and data['dirty_rate_limit'] > 0):
|
||||||
|
sql += "\n-- Following query will be executed in a separate transaction\n"
|
||||||
|
sql += render_template("/".join([self.template_path, 'update.sql']), data=data, conn=self.conn)
|
||||||
|
|
||||||
|
return sql
|
||||||
|
except Exception as e:
|
||||||
|
return internal_server_error(errormsg=str(e))
|
||||||
|
|
||||||
|
@check_precondition
|
||||||
|
def sql(self, gid, sid, rg_id):
|
||||||
|
"""
|
||||||
|
This function will generate sql for sql pane
|
||||||
|
|
||||||
|
Args:
|
||||||
|
gid: Server Group ID
|
||||||
|
sid: Server ID
|
||||||
|
rg_id: Resource Group ID
|
||||||
|
"""
|
||||||
|
sql = render_template("/".join([self.template_path, 'properties.sql']), rgid=rg_id)
|
||||||
|
status, res = self.conn.execute_dict(sql)
|
||||||
|
if not status:
|
||||||
|
return internal_server_error(errormsg=res)
|
||||||
|
|
||||||
|
# Making copy of output for future use
|
||||||
|
old_data = dict(res['rows'][0])
|
||||||
|
|
||||||
|
sql = render_template("/".join([self.template_path, 'create.sql']), display_comments=True,
|
||||||
|
rgname=old_data['name'], conn=self.conn)
|
||||||
|
sql += "\n"
|
||||||
|
sql += render_template("/".join([self.template_path, 'update.sql']), data=old_data, conn=self.conn)
|
||||||
|
|
||||||
|
return ajax_response(response=sql)
|
||||||
|
|
||||||
|
ResourceGroupView.register_node_view(blueprint)
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 966 B |
@@ -0,0 +1,143 @@
|
|||||||
|
define(
|
||||||
|
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify', 'pgadmin.browser.collection'],
|
||||||
|
function($, _, S, pgAdmin, pgBrowser, alertify) {
|
||||||
|
|
||||||
|
// Extend the browser's collection class for resource group collection
|
||||||
|
if (!pgBrowser.Nodes['coll-resource_group']) {
|
||||||
|
var resourcegroups = pgAdmin.Browser.Nodes['coll-resource_group'] =
|
||||||
|
pgAdmin.Browser.Collection.extend({
|
||||||
|
node: 'resource_group',
|
||||||
|
label: '{{ _('Resource Groups') }}',
|
||||||
|
type: 'coll-resource_group',
|
||||||
|
columns: ['name']
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend the browser's node class for resource group node
|
||||||
|
if (!pgBrowser.Nodes['resource_group']) {
|
||||||
|
pgAdmin.Browser.Nodes['resource_group'] = pgAdmin.Browser.Node.extend({
|
||||||
|
parent_type: 'server',
|
||||||
|
type: 'resource_group',
|
||||||
|
label: '{{ _('Resource Group') }}',
|
||||||
|
hasSQL: true,
|
||||||
|
canDrop: true,
|
||||||
|
Init: function() {
|
||||||
|
|
||||||
|
// Avoid multiple registration of menus
|
||||||
|
if (this.initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
// Creating menu for the resource group node
|
||||||
|
pgBrowser.add_menus([{
|
||||||
|
name: 'create_resourcegroup_on_server', node: 'server', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('Resource Group...') }}',
|
||||||
|
icon: 'wcTabIcon icon-resource_group', data: {action: 'create'},
|
||||||
|
/* Function is used to check the server type and version.
|
||||||
|
* Resource Group only supported in PPAS 9.4 and above.
|
||||||
|
*/
|
||||||
|
enable: function() {
|
||||||
|
var server_obj = arguments[0];
|
||||||
|
if (server_obj.server_type === 'ppas' && server_obj.version >= 90400)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
name: 'create_resource_group_on_coll', node: 'coll-resource_group', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('Resource Group...') }}',
|
||||||
|
icon: 'wcTabIcon icon-resource_group', data: {action: 'create'}
|
||||||
|
},{
|
||||||
|
name: 'create_resource_group', node: 'resource_group', module: this,
|
||||||
|
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||||
|
category: 'create', priority: 4, label: '{{ _('Resource Group...') }}',
|
||||||
|
icon: 'wcTabIcon icon-resource_group', data: {action: 'create'}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Defining model for resource group node
|
||||||
|
model: pgAdmin.Browser.Node.Model.extend({
|
||||||
|
defaults: {
|
||||||
|
name: undefined,
|
||||||
|
cpu_rate_limit: 0.0,
|
||||||
|
dirty_rate_limit: 0.0
|
||||||
|
},
|
||||||
|
|
||||||
|
// Defining schema for the resource group node
|
||||||
|
schema: [{
|
||||||
|
id: 'name', label: '{{ _('Group Name') }}', cell: 'string',
|
||||||
|
type: 'text',
|
||||||
|
},{
|
||||||
|
id: 'cpu_rate_limit', label:'{{ _('CPU Rate Limit') }}', cell: 'string',
|
||||||
|
type: 'numeric', min:0, max:16777216
|
||||||
|
},{
|
||||||
|
id: 'dirty_rate_limit', label:'{{ _('Dirty Rate Limit') }}', cell: 'string',
|
||||||
|
type: 'numeric', min:0, max:16777216
|
||||||
|
}],
|
||||||
|
|
||||||
|
/* validate function is used to validate the input given by
|
||||||
|
* the user. In case of error, message will be displayed on
|
||||||
|
* the GUI for the respective control.
|
||||||
|
*/
|
||||||
|
validate: function(keys) {
|
||||||
|
|
||||||
|
/* Check whether 'name' is present in 'keys', if it is present
|
||||||
|
* it means there is a change in that field from the GUI, so we
|
||||||
|
* need to validate it.
|
||||||
|
*/
|
||||||
|
if (_.indexOf(keys, 'name') >= 0) {
|
||||||
|
var name = this.get('name');
|
||||||
|
if (_.isUndefined(name) || _.isNull(name) ||
|
||||||
|
String(name).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
var msg = '{{ _('Group Name can not be empty!') }}';
|
||||||
|
this.errorModel.set('name', msg);
|
||||||
|
return msg;
|
||||||
|
} else {
|
||||||
|
this.errorModel.unset('name');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether 'cpu_rate_limit' is present in 'keys', if it is present
|
||||||
|
* it means there is a change in that field from the GUI, so we
|
||||||
|
* need to validate it.
|
||||||
|
*/
|
||||||
|
if (_.indexOf(keys, 'cpu_rate_limit') >= 0) {
|
||||||
|
var cpu_rate_limit = this.get('cpu_rate_limit');
|
||||||
|
if (_.isUndefined(cpu_rate_limit) || _.isNull(cpu_rate_limit) ||
|
||||||
|
String(cpu_rate_limit).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
var msg = '{{ _('CPU Rate Limit can not be empty!') }}';
|
||||||
|
this.errorModel.set('cpu_rate_limit', msg);
|
||||||
|
return msg;
|
||||||
|
} else {
|
||||||
|
this.errorModel.unset('cpu_rate_limit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check whether 'dirty_rate_limit' is present in 'keys', if it is present
|
||||||
|
* it means there is a change in that field from the GUI, so we
|
||||||
|
* need to validate it.
|
||||||
|
*/
|
||||||
|
if (_.indexOf(keys, 'dirty_rate_limit') >= 0) {
|
||||||
|
var dirty_rate_limit = this.get('dirty_rate_limit');
|
||||||
|
if (_.isUndefined(dirty_rate_limit) || _.isNull(dirty_rate_limit) ||
|
||||||
|
String(dirty_rate_limit).replace(/^\s+|\s+$/g, '') == '') {
|
||||||
|
var msg = '{{ _('Dirty Rate Limit can not be empty!') }}';
|
||||||
|
this.errorModel.set('dirty_rate_limit', msg);
|
||||||
|
return msg;
|
||||||
|
} else {
|
||||||
|
this.errorModel.unset('dirty_rate_limit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return pgBrowser.Nodes['coll-resource_group'];
|
||||||
|
});
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% if display_comments %}
|
||||||
|
-- RESOURCE GROUP: {{rgname}}
|
||||||
|
|
||||||
|
-- DROP RESOURCE GROUP {{ conn|qtIdent(rgname) }}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{# ============= Create the resource group============= #}
|
||||||
|
{% if rgname %}
|
||||||
|
CREATE RESOURCE GROUP {{ conn|qtIdent(rgname) }};
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{# ============= Below SQL will get the resource group name using oid ============= #}
|
||||||
|
{% if rgid %}
|
||||||
|
SELECT rgrpname FROM edb_resource_group WHERE oid = {{rgid}}::int;
|
||||||
|
{% endif %}
|
||||||
|
{# ============= Below SQL will drop the resource group ============= #}
|
||||||
|
{% if rgname %}
|
||||||
|
DROP RESOURCE GROUP {{ conn|qtIdent(rgname) }};
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{# ============= Get the resource group oid ============= #}
|
||||||
|
{% if rgname %}
|
||||||
|
SELECT oid FROM edb_resource_group WHERE rgrpname = {{ rgname|qtLiteral }};
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
SELECT oid, rgrpname AS name, rgrpcpuratelimit AS cpu_rate_limit, rgrpdirtyratelimit AS dirty_rate_limit
|
||||||
|
FROM edb_resource_group
|
||||||
|
{% if rgid %}
|
||||||
|
WHERE oid={{rgid}}::int
|
||||||
|
{% endif %}
|
||||||
|
ORDER BY rgrpname
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{# ============= Update resource group name ============= #}
|
||||||
|
{% if newname %}
|
||||||
|
ALTER RESOURCE GROUP {{ conn|qtIdent(oldname) }} RENAME TO {{ conn|qtIdent(newname) }};
|
||||||
|
{% endif %}
|
||||||
|
{# ============= Update resource group cpu_rate_limit and dirty_rate_limit ============= #}
|
||||||
|
{% if data %}
|
||||||
|
ALTER RESOURCE GROUP {{ conn|qtIdent(data.name) }}
|
||||||
|
SET cpu_rate_limit = {{data.cpu_rate_limit|default(0)}}, dirty_rate_limit = {{data.dirty_rate_limit|default(0)}};
|
||||||
|
{% endif %}
|
||||||
@@ -836,8 +836,11 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) {
|
|||||||
} else if (view.model.get('name')) {
|
} else if (view.model.get('name')) {
|
||||||
tree.setLabel(item, {label: view.model.get("name")});
|
tree.setLabel(item, {label: view.model.get("name")});
|
||||||
}
|
}
|
||||||
|
tree.deselect(item);
|
||||||
panel.$container.removeAttr('action-mode');
|
panel.$container.removeAttr('action-mode');
|
||||||
setTimeout(function() { closePanel(); }, 0);
|
setTimeout(function() { closePanel(); }, 0);
|
||||||
|
|
||||||
|
setTimeout(function() { tree.select(item, {focus: true}); }, 10);
|
||||||
},
|
},
|
||||||
saveNewNode = function() {
|
saveNewNode = function() {
|
||||||
/* TODO:: Create new tree node for this */
|
/* TODO:: Create new tree node for this */
|
||||||
|
|||||||
Reference in New Issue
Block a user