Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py746 linesDownload Raw Back to resource_groups
1##########################################################################2#3# pgAdmin 4 - PostgreSQL Tools4#5# Copyright (C) 2013 - 2024, The pgAdmin Development Team6# This software is released under the PostgreSQL Licence7#8##########################################################################9 10"""Implements Resource Groups for PPAS 9.4 and above"""11 12import json13from functools import wraps14 15from pgadmin.browser.server_groups import servers16from flask import render_template, make_response, request, jsonify17from flask_babel import gettext18from pgadmin.browser.collection import CollectionNodeModule19from pgadmin.browser.utils import NodeView20from pgadmin.utils.ajax import make_json_response, \21    make_response as ajax_response, internal_server_error, gone22from pgadmin.utils.ajax import precondition_required23from pgadmin.utils.driver import get_driver24from config import PG_DEFAULT_DRIVER25from pgadmin.utils.constants import DATABASE_LAST_SYSTEM_OID26 27 28class ResourceGroupModule(CollectionNodeModule):29    """30     class ResourceGroupModule(CollectionNodeModule)31 32        A module class for Resource Group node derived from33        CollectionNodeModule.34 35    Methods:36    -------37    * __init__(*args, **kwargs)38      - Method is used to initialize the ResourceGroupModule and it's39      base module.40 41    * backend_supported(manager, **kwargs)42      - This function is used to check the database server type and version.43        Resource Group only supported in PPAS 9.4 and above.44 45    * get_nodes(gid, sid, did)46      - Method is used to generate the browser collection node.47 48    * node_inode()49      - Method is overridden from its base class to make the node as leaf node.50 51    * script_load()52      - Load the module script for resource group, when any of the server53      node is initialized.54    """55 56    _NODE_TYPE = 'resource_group'57    _COLLECTION_LABEL = gettext("Resource Groups")58 59    def __init__(self, *args, **kwargs):60        """61        Method is used to initialize the ResourceGroupModule and62        it's base module.63 64        Args:65            *args:66            **kwargs:67        """68        super().__init__(*args, **kwargs)69 70        self.min_ver = 9040071        self.max_ver = None72        self.server_type = ['ppas']73 74    def get_nodes(self, gid, sid):75        """76        Method is used to generate the browser collection node77 78        Args:79            gid: Server Group ID80            sid: Server ID81        """82        if self.has_nodes(83            sid, None,84                base_template_path=ResourceGroupView.BASE_TEMPLATE_PATH):85            yield self.generate_browser_collection_node(sid)86 87    @property88    def node_inode(self):89        """90        Override this property to make the node as leaf node.91 92        Returns: False as this is the leaf node93        """94        return False95 96    @property97    def script_load(self):98        """99        Load the module script for resource group, when any of the server100        node is initialized.101 102        Returns: node type of the server module.103        """104        return servers.ServerModule.NODE_TYPE105 106    @property107    def module_use_template_javascript(self):108        """109        Returns whether Jinja2 template is used for generating the javascript110        module.111        """112        return False113 114 115blueprint = ResourceGroupModule(__name__)116 117 118class ResourceGroupView(NodeView):119    """120    class ResourceGroupView(NodeView)121 122        A view class for resource group node derived from NodeView.123        This class is responsible for all the stuff related to view like124        create/update/delete resource group, showing properties of resource125        group node, showing sql in sql pane.126 127    Methods:128    -------129    * __init__(**kwargs)130      - Method is used to initialize the ResourceGroupView and it's base view.131 132    * check_precondition()133      - This function will behave as a decorator which will checks134        database connection before running view, it will also attaches135        manager,conn & template_path properties to self136 137    * list()138      - This function is used to list all the resource group nodes within139      that collection.140 141    * nodes()142      - This function will used to create all the child node within that143      collection. Here it will create all the resource group node.144 145    * properties(gid, sid, did, rg_id)146      - This function will show the properties of the selected resource147      group node148 149    * create(gid, sid, did, rg_id)150      - This function will create the new resource group object151 152    * update(gid, sid, did, rg_id)153      - This function will update the data for the selected resource group node154 155    * delete(self, gid, sid, rg_id):156      - This function will drop the resource group object157 158    * msql(gid, sid, did, rg_id)159      - This function is used to return modified SQL for the selected160      resource group node161 162    * get_sql(data, rg_id)163      - This function will generate sql from model data164 165    * sql(gid, sid, did, rg_id):166      - This function will generate sql to show it in sql pane for the167      selected resource group node.168    """169 170    node_type = blueprint.node_type171    BASE_TEMPLATE_PATH = 'resource_groups/sql/#{0}#'172    _PROPERTIES_SQL = 'properties.sql'173    _CREATE_SQL = 'create.sql'174    _UPDATE_SQL = 'update.sql'175    _NODES_SQL = 'nodes.sql'176    _DELETE_SQL = 'delete.sql'177 178    parent_ids = [179        {'type': 'int', 'id': 'gid'},180        {'type': 'int', 'id': 'sid'}181    ]182    ids = [183        {'type': 'int', 'id': 'rg_id'}184    ]185 186    operations = dict({187        'obj': [188            {'get': 'properties', 'delete': 'delete', 'put': 'update'},189            {'get': 'list', 'post': 'create', 'delete': 'delete'}190        ],191        'nodes': [{'get': 'node'}, {'get': 'nodes'}],192        'sql': [{'get': 'sql'}],193        'msql': [{'get': 'msql'}, {'get': 'msql'}],194        'stats': [{'get': 'statistics'}],195        'dependency': [{'get': 'dependencies'}],196        'dependent': [{'get': 'dependents'}]197    })198 199    def __init__(self, **kwargs):200        """201        Method is used to initialize the ResourceGroupView and it's base view.202        Also initialize all the variables create/used dynamically like conn,203        template_path.204 205        Args:206            **kwargs:207        """208        self.conn = None209        self.template_path = None210 211        super().__init__(**kwargs)212 213    def check_precondition(f):214        """215        This function will behave as a decorator which will checks216        database connection before running view, it will also attaches217        manager,conn & template_path properties to self218        """219 220        @wraps(f)221        def wrap(*args, **kwargs):222            # Here args[0] will hold self & kwargs will hold gid,sid,did223            self = args[0]224            self.driver = get_driver(PG_DEFAULT_DRIVER)225            self.manager = self.driver.connection_manager(kwargs['sid'])226            self.conn = self.manager.connection()227 228            self.datistemplate = False229            if (230                self.manager.db_info is not None and231                self.manager.did in self.manager.db_info and232                'datistemplate' in self.manager.db_info[self.manager.did]233            ):234                self.datistemplate = self.manager.db_info[235                    self.manager.did]['datistemplate']236 237            if not self.conn.connected():238                return precondition_required(239                    gettext(240                        "Connection to the server has been lost."241                    )242                )243            self.sql_path = self.BASE_TEMPLATE_PATH.format(244                self.manager.version245            )246            return f(*args, **kwargs)247 248        return wrap249 250    @check_precondition251    def list(self, gid, sid):252        """253        This function is used to list all the resource group nodes within254        that collection.255 256        Args:257            gid: Server Group ID258            sid: Server ID259        """260        sql = render_template("/".join([self.sql_path, self._PROPERTIES_SQL]))261        status, res = self.conn.execute_dict(sql)262 263        if not status:264            return internal_server_error(errormsg=res)265        return ajax_response(266            response=res['rows'],267            status=200268        )269 270    @check_precondition271    def node(self, gid, sid, rg_id):272        """273        This function will used to create all the child node within that274        collection. Here it will create all the resource group node.275 276        Args:277            gid: Server Group ID278            sid: Server ID279        """280 281        sql = render_template("/".join([self.sql_path, self._NODES_SQL]),282                              rgid=rg_id)283        status, result = self.conn.execute_2darray(sql)284        if not status:285            return internal_server_error(errormsg=result)286 287        if len(result['rows']) == 0:288            return gone(gettext("""Could not find the resource group."""))289 290        res = self.blueprint.generate_browser_node(291            result['rows'][0]['oid'],292            sid,293            result['rows'][0]['name'],294            icon="icon-resource_group"295        )296 297        return make_json_response(298            data=res,299            status=200300        )301 302    @check_precondition303    def nodes(self, gid, sid):304        """305        This function will used to create all the child node within that306        collection. Here it will create all the resource group node.307 308        Args:309            gid: Server Group ID310            sid: Server ID311        """312        res = []313        sql = render_template("/".join([self.sql_path, self._NODES_SQL]))314        status, result = self.conn.execute_2darray(sql)315        if not status:316            return internal_server_error(errormsg=result)317 318        for row in result['rows']:319            res.append(320                self.blueprint.generate_browser_node(321                    row['oid'],322                    sid,323                    row['name'],324                    icon="icon-resource_group"325                ))326 327        return make_json_response(328            data=res,329            status=200330        )331 332    @check_precondition333    def properties(self, gid, sid, rg_id):334        """335        This function will show the properties of the selected resource336        group node.337 338        Args:339            gid: Server Group ID340            sid: Server ID341            rg_id: Resource Group ID342        """343        sql = render_template(344            "/".join([self.sql_path, self._PROPERTIES_SQL]), rgid=rg_id)345        status, res = self.conn.execute_dict(sql)346 347        if not status:348            return internal_server_error(errormsg=res)349 350        if len(res['rows']) == 0:351            return gone(gettext("""Could not find the resource group."""))352 353        res['rows'][0]['is_sys_obj'] = (354            res['rows'][0]['oid'] <= DATABASE_LAST_SYSTEM_OID or355            self.datistemplate)356 357        return ajax_response(358            response=res['rows'][0],359            status=200360        )361 362    @staticmethod363    def _check_req_parameters(data, required_args):364        """365        This function is used to check the request parameter.366        :param data:367        :param required_args:368        :return:369        """370        for arg in required_args:371            if arg not in data:372                return True, make_json_response(373                    status=410,374                    success=0,375                    errormsg=gettext(376                        "Could not find the required parameter ({})."377                    ).format(arg)378                )379        return False, ''380 381    @check_precondition382    def create(self, gid, sid):383        """384        This function will create the new resource group object385 386        Args:387            gid: Server Group ID388            sid: Server ID389        """390        required_args = [391            'name'392        ]393 394        data = request.form if request.form else json.loads(395            request.data396        )397 398        is_error, errmsg = ResourceGroupView._check_req_parameters(399            data, required_args)400 401        if is_error:402            return errmsg403 404        try:405            # Below logic will create new resource group406            sql = render_template(407                "/".join([self.sql_path, self._CREATE_SQL]),408                rgname=data['name'], conn=self.conn409            )410            if sql and sql.strip('\n') and sql.strip(' '):411                status, res = self.conn.execute_scalar(sql)412                if not status:413                    return internal_server_error(errormsg=res)414            # Below logic will update the cpu_rate_limit and dirty_rate_limit415            # for resource group, we need to add this logic because in416            # resource group you can't run multiple commands in one417            # transaction.418            sql = render_template(419                "/".join([self.sql_path, self._UPDATE_SQL]),420                data=data, conn=self.conn421            )422            # Checking if we are not executing empty query423            if sql and sql.strip('\n') and sql.strip(' '):424                status, res = self.conn.execute_scalar(sql)425                if not status:426                    return internal_server_error(errormsg=res)427            # Below logic is used to fetch the oid of the newly created428            # resource group429            sql = render_template(430                "/".join([self.sql_path, 'getoid.sql']),431                rgname=data['name'], conn=self.conn432            )433            # Checking if we are not executing empty query434            rg_id = 0435            if sql and sql.strip('\n') and sql.strip(' '):436                status, rg_id = self.conn.execute_scalar(sql)437                if not status:438                    return internal_server_error(errormsg=rg_id)439 440            return jsonify(441                node=self.blueprint.generate_browser_node(442                    rg_id,443                    sid,444                    data['name'],445                    icon="icon-resource_group"446                )447            )448        except Exception as e:449            return internal_server_error(errormsg=str(e))450 451    def _check_cpu_and_dirty_rate_limit(self, data, old_data):452        """453        Below logic will update the cpu_rate_limit and dirty_rate_limit454        for resource group we need to add this logic because in455        resource group you can't run multiple commands in one456        transaction.457        :param data:458        :param old_data:459        :return:460        """461 462        # Below logic will update the cpu_rate_limit and dirty_rate_limit463        # for resource group we need to add this logic because in464        # resource group you can't run multiple commands in one465        # transaction.466        if data['cpu_rate_limit'] != old_data['cpu_rate_limit'] or \467                data['dirty_rate_limit'] != old_data['dirty_rate_limit']:468            sql = render_template(469                "/".join([self.sql_path, self._UPDATE_SQL]),470                data=data, conn=self.conn471            )472            if sql and sql.strip('\n') and sql.strip(' '):473                status, res = self.conn.execute_scalar(sql)474                if not status:475                    return internal_server_error(errormsg=res)476 477    @check_precondition478    def update(self, gid, sid, rg_id):479        """480        This function will update the data for the selected resource group node481 482        Args:483            gid: Server Group ID484            sid: Server ID485            rg_id: Resource Group ID486        """487        required_args = [488            'name', 'cpu_rate_limit', 'dirty_rate_limit'489        ]490        data = request.form if request.form else json.loads(491            request.data492        )493 494        try:495            sql = render_template(496                "/".join([self.sql_path, self._PROPERTIES_SQL]), rgid=rg_id)497            status, res = self.conn.execute_dict(sql)498            if not status:499                return internal_server_error(errormsg=res)500            old_data = res['rows'][0]501            for arg in required_args:502                if arg not in data:503                    data[arg] = old_data[arg]504 505            if data['name'] != old_data['name']:506                sql = render_template(507                    "/".join([self.sql_path, self._UPDATE_SQL]),508                    oldname=old_data['name'], newname=data['name'],509                    conn=self.conn510                )511                if sql and sql.strip('\n') and sql.strip(' '):512                    status, res = self.conn.execute_scalar(sql)513                    if not status:514                        return internal_server_error(errormsg=res)515 516            # Below logic will update the cpu_rate_limit and dirty_rate_limit517            # for resource group we need to add this logic because in518            # resource group you can't run multiple commands in one519            # transaction.520            self._check_cpu_and_dirty_rate_limit(data, old_data)521 522            return jsonify(523                node=self.blueprint.generate_browser_node(524                    rg_id,525                    sid,526                    data['name'],527                    icon="icon-%s" % self.node_type528                )529            )530 531        except Exception as e:532            return internal_server_error(errormsg=str(e))533 534    @check_precondition535    def delete(self, gid, sid, rg_id=None):536        """537        This function will drop the resource group object538 539        Args:540            gid: Server Group ID541            sid: Server ID542            rg_id: Resource Group ID543        """544        if rg_id is None:545            data = request.form if request.form else json.loads(546                request.data547            )548        else:549            data = {'ids': [rg_id]}550 551        try:552            for rg_id in data['ids']:553                # Get name for resource group from rg_id554                sql = render_template(555                    "/".join([self.sql_path, self._DELETE_SQL]),556                    rgid=rg_id, conn=self.conn557                )558                status, rgname = self.conn.execute_scalar(sql)559                if not status:560                    return internal_server_error(errormsg=rgname)561 562                if rgname is None:563                    return make_json_response(564                        success=0,565                        errormsg=gettext(566                            'Error: Object not found.'567                        ),568                        info=gettext(569                            'The specified resource group '570                            'could not be found.\n'571                        )572                    )573 574                # drop resource group575                sql = render_template(576                    "/".join([self.sql_path, self._DELETE_SQL]),577                    rgname=rgname, conn=self.conn578                )579                status, res = self.conn.execute_scalar(sql)580                if not status:581                    return internal_server_error(errormsg=res)582 583            return make_json_response(584                success=1,585                info=gettext("Resource Group dropped")586            )587 588        except Exception as e:589            return internal_server_error(errormsg=str(e))590 591    @check_precondition592    def msql(self, gid, sid, rg_id=None):593        """594        This function is used to return modified SQL for the selected595        resource group node596 597        Args:598            gid: Server Group ID599            sid: Server ID600            rg_id: Resource Group ID601        """602        data = dict()603        for k, v in request.args.items():604            try:605                data[k] = json.loads(v)606            except ValueError:607                data[k] = v608 609        sql, _ = self.get_sql(data, rg_id)610        # Most probably this is due to error611        if not isinstance(sql, str):612            return sql613 614        sql = sql.strip('\n').strip(' ')615 616        if sql == '':617            sql = "--modified SQL"618        return make_json_response(619            data=sql,620            status=200621        )622 623    def _get_update_sql(self, rg_id, data, required_args):624        """625        This function is used to get the sql for resource group626        :param rg_id:627        :param data:628        :param required_args:629        :return:630        """631        sql = render_template(632            "/".join([self.sql_path, self._PROPERTIES_SQL]), rgid=rg_id)633        status, res = self.conn.execute_dict(sql)634        if not status:635            return internal_server_error(errormsg=res)636 637        if len(res['rows']) == 0:638            return gone(639                gettext("The specified resource group could not be found.")640            )641        old_data = res['rows'][0]642        for arg in required_args:643            if arg not in data:644                data[arg] = old_data[arg]645 646        sql = ''647        name_changed = False648        if data['name'] != old_data['name']:649            name_changed = True650            sql = render_template(651                "/".join([self.sql_path, self._UPDATE_SQL]),652                oldname=old_data['name'], newname=data['name'],653                conn=self.conn654            )655        if data['cpu_rate_limit'] != old_data['cpu_rate_limit'] or \656                data['dirty_rate_limit'] != old_data['dirty_rate_limit']:657            if name_changed:658                sql += "\n-- Following query will be executed in a " \659                       "separate transaction\n"660            sql += render_template(661                "/".join([self.sql_path, self._UPDATE_SQL]),662                data=data, conn=self.conn663            )664 665        return sql, old_data['name']666 667    def get_sql(self, data, rg_id=None):668        """669        This function will generate sql from model data670 671        Args:672            data: Contains the value of name, cpu_rate_limit, dirty_rate_limit673            rg_id: Resource Group Id674        """675        required_args = [676            'name', 'cpu_rate_limit', 'dirty_rate_limit'677        ]678 679        old_name = ''680        if rg_id is not None:681            # Get sql for Resource group by ID.682            sql, old_name = self._get_update_sql(rg_id, data, required_args)683        else:684            sql = render_template(685                "/".join([self.sql_path, self._CREATE_SQL]),686                rgname=data['name'], conn=self.conn687            )688 689            cpu_rate_limit_flag = False690            dirty_rate_limit_flag = False691            if 'cpu_rate_limit' in data and data['cpu_rate_limit'] >= 0:692                cpu_rate_limit_flag = True693 694            if 'dirty_rate_limit' in data and data['dirty_rate_limit'] >= 0:695                dirty_rate_limit_flag = True696 697            if cpu_rate_limit_flag or dirty_rate_limit_flag:698                sql += "\n-- Following query will be executed in a " \699                       "separate transaction\n"700                sql += render_template(701                    "/".join([self.sql_path, self._UPDATE_SQL]),702                    data=data, conn=self.conn703                )704 705        return sql, data['name'] if 'name' in data else old_name706 707    @check_precondition708    def sql(self, gid, sid, rg_id):709        """710        This function will generate sql for sql pane711 712        Args:713            gid: Server Group ID714            sid: Server ID715            rg_id: Resource Group ID716        """717        sql = render_template(718            "/".join([self.sql_path, self._PROPERTIES_SQL]), rgid=rg_id719        )720        status, res = self.conn.execute_dict(sql)721        if not status:722            return internal_server_error(errormsg=res)723        if len(res['rows']) == 0:724            return gone(725                gettext("The specified resource group could not be found.")726            )727 728        # Making copy of output for future use729        old_data = dict(res['rows'][0])730 731        sql = render_template(732            "/".join([self.sql_path, self._CREATE_SQL]),733            display_comments=True,734            rgname=old_data['name'], conn=self.conn735        )736        sql += "\n"737        sql += render_template(738            "/".join([self.sql_path, self._UPDATE_SQL]),739            data=old_data, conn=self.conn740        )741 742        return ajax_response(response=sql)743 744 745ResourceGroupView.register_node_view(blueprint)746 
codekingpro/portable-devtools · Team Ai