PNG  IHDRxsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<,tEXtComment File Manager

File Manager

Path: /opt/cloudlinux/venv/lib/python3.11/site-packages/xray/internal/

Viewing File: nginx_utils.py

# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

"""
This module contains utilities for ea-nginx cache disabling during tracing
"""
import json
import logging
import os.path
import subprocess
from dataclasses import dataclass
from enum import Enum
from typing import Optional

from xray import gettext as _

from .constants import nginx_cache_stat_storage, allow_disable_nginx_cache
from .exceptions import XRayError
from .utils import dbm_storage

logger = logging.getLogger('nginx_utils')


class Cache(Enum):
    """Cache statuses enum"""
    ENABLED = 1
    DISABLED = 0


@dataclass
class SavedState:
    """"""
    initial: Cache
    active_tasks: int = 0


@dataclass
class NginxUserCache:
    """
    Class which implements reading/writing the user's cache status
    from the local storage
    """
    username: str

    @property
    def current_status(self) -> Cache:
        """
        Detect if cache is enabled or disabled for user
        using cPanel's guidelines
        https://docs.cpanel.net/knowledge-base/web-services/nginx-with-reverse-proxy/#third-party-integration
        """
        global_file = '/etc/nginx/ea-nginx/cache.json'
        user_file = f'/var/cpanel/userdata/{self.username}/nginx-cache.json'
        if not os.path.isfile(global_file):
            # this means that ea-nginx is not installed
            # thus cache status can be safety interpreted as DISABLED
            # as we do nothing in such a case
            return Cache.DISABLED

        if os.path.isfile(user_file):
            # try to find cache setting in user's file
            user_setting = self.get_cache_status_from_file(user_file)
            if user_setting is not None:
                return Cache(user_setting)

        global_setting = self.get_cache_status_from_file(global_file)
        if global_setting is not None:
            return Cache(global_setting)
        # cache is enabled by default
        return Cache.ENABLED

    @property
    def is_enabled(self) -> bool:
        """Check if cache is enabled for user"""
        return self.current_status is Cache.ENABLED

    @property
    def saved_state(self) -> SavedState:
        """
        Get saved state (initial cache setting and active tasks count)
        of nginx cache manipulations
        """
        try:
            with dbm_storage(nginx_cache_stat_storage,
                             is_shelve=True) as _nginx_cache_stat:
                return _nginx_cache_stat[self.username]
        except RuntimeError as e:
            logger.warning(f'Failed to get saved state of nginx cache with %s',
                           e)
        except KeyError:
            logger.info(f'No nginx cache entry found for {self.username}')
        # if failed to find entry in file or error occurred
        # consider initial state as DISABLED with no active tasks
        return SavedState(Cache.DISABLED)

    @saved_state.setter
    def saved_state(self, value: SavedState) -> None:
        """
        Save state of nginx cache manipulations -- initial cache setting
        and active tasks count
        """
        try:
            with dbm_storage(nginx_cache_stat_storage,
                             is_shelve=True) as _nginx_cache_stat:
                _nginx_cache_stat[self.username] = value
        except RuntimeError as e:
            logger.warning('Failed to save state of nginx cache with %s', e)

    @saved_state.deleter
    def saved_state(self) -> None:
        """
        Delete the record of state of nginx cache manipulations --
        initial cache setting and active tasks count
        """
        try:
            with dbm_storage(nginx_cache_stat_storage,
                             is_shelve=True) as _nginx_cache_stat:
                del _nginx_cache_stat[self.username]
        except RuntimeError as e:
            logger.warning('Failed to delete state of nginx cache with %s', e)

    def set(self, enum_member: Cache) -> None:
        """Enable or disable cache for user"""
        cmd = f"/usr/local/cpanel/scripts/ea-nginx cache {self.username} --enabled={enum_member.value}"
        try:
            subprocess.run(cmd.split(), check=True, text=True,
                           capture_output=True)
        except subprocess.CalledProcessError as e:
            logger.info(
                'Failed command %s details: exitcode %i, stdout %s, stderr %s',
                e.args, e.returncode, e.stdout, e.stderr)
            raise XRayError(str(e), flag='warning',
                            extra={'out': e.stdout, 'err': e.stderr})
        except (OSError, ValueError, subprocess.SubprocessError) as e:
            raise XRayError(
                _('Failed to set nginx cache as {} with {}'.format(str(enum_member.name), str(e))),
                flag='warning')

    @staticmethod
    def get_cache_status_from_file(filename: str) -> Optional[bool]:
        """Get the 'enabled' key from given file"""
        try:
            with open(filename) as _f:
                try:
                    data = json.load(_f)
                except json.JSONDecodeError as e:
                    logger.error('Invalid JSON from %s: %s',
                                 filename, str(e),
                                 extra={'contents': _f.read()})
                else:
                    return data.get('enabled')
        except (IOError, OSError) as e:
            logger.error('Failed to read file %s with %s',
                         filename, str(e))

    def disable(self) -> None:
        """
        If cache is enabled for user, disable it.
        Save retrieved cache status.
        For ENABLED initial status increment number of active tasks.
        Skip disabling cache if it is disallowed in constants file
        """
        if not allow_disable_nginx_cache:
            # terminate operation if cache disabling is disallowed
            return

        _state = self.saved_state

        if self.is_enabled:
            try:
                self.set(Cache.DISABLED)
            except XRayError:
                pass
            else:
                _state.initial = Cache.ENABLED

        if _state.initial is Cache.ENABLED:
            _state.active_tasks += 1

        self.saved_state = _state

    def restore(self) -> None:
        """
        For ENABLED initial status decrement number of active tasks.
        Restore the cache state according to saved status:
        - enable if it was enabled and number of active tasks <= 0,
        - do nothing if it was enabled, but number of active tasks > 0,
        - do nothing if it was disabled.
        Clear the record if enable has taken place.
        Skip operation if cache disabling is disallowed in constants file
        """
        _state = self.saved_state

        if _state.initial is Cache.ENABLED:
            _state.active_tasks -= 1

            if _state.active_tasks <= 0:
                try:
                    self.set(Cache.ENABLED)
                except XRayError:
                    pass
                del self.saved_state
                return

        self.saved_state = _state
b IDATxytVսϓ22 A@IR :hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-EIENT ;@xT.i%-X}SvS5.r/UHz^_$-W"w)Ɗ/@Z &IoX P$K}JzX:;` &, ŋui,e6mX ԵrKb1ԗ)DADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADADA݀!I*]R;I2$eZ#ORZSrr6mteffu*((Pu'v{DIߔ4^pIm'77WEEE;vƎ4-$]'RI{\I&G :IHJ DWBB=\WR޽m o$K(V9ABB.}jѢv`^?IOȅ} ڶmG}T#FJ`56$-ھ}FI&v;0(h;Б38CӧOWf!;A i:F_m9s&|q%=#wZprrrla A &P\\СC[A#! {olF} `E2}MK/vV)i{4BffV\|ۭX`b@kɶ@%i$K z5zhmX[IXZ` 'b%$r5M4º/l ԃߖxhʔ)[@=} K6IM}^5k㏷݆z ΗÿO:gdGBmyT/@+Vɶ纽z񕏵l.y޴it뭷zV0[Y^>Wsqs}\/@$(T7f.InݺiR$푔n.~?H))\ZRW'Mo~v Ov6oԃxz! S,&xm/yɞԟ?'uaSѽb,8GלKboi&3t7Y,)JJ c[nzӳdE&KsZLӄ I?@&%ӟ۶mSMMњ0iؐSZ,|J+N ~,0A0!5%Q-YQQa3}$_vVrf9f?S8`zDADADADADADADADADAdqP,تmMmg1V?rSI꒟]u|l RCyEf٢9 jURbztѰ!m5~tGj2DhG*{H9)꒟ר3:(+3\?/;TUݭʴ~S6lڧUJ*i$d(#=Yݺd{,p|3B))q:vN0Y.jkק6;SɶVzHJJЀ-utѹսk>QUU\޲~]fFnK?&ߡ5b=z9)^|u_k-[y%ZNU6 7Mi:]ۦtk[n X(e6Bb."8cۭ|~teuuw|ήI-5"~Uk;ZicEmN/:]M> cQ^uiƞ??Ңpc#TUU3UakNwA`:Y_V-8.KKfRitv޲* 9S6ֿj,ՃNOMߤ]z^fOh|<>@Å5 _/Iu?{SY4hK/2]4%it5q]GGe2%iR| W&f*^]??vq[LgE_3f}Fxu~}qd-ږFxu~I N>\;͗O֊:̗WJ@BhW=y|GgwܷH_NY?)Tdi'?խwhlmQi !SUUsw4kӺe4rfxu-[nHtMFj}H_u~w>)oV}(T'ebʒv3_[+vn@Ȭ\S}ot}w=kHFnxg S 0eޢm~l}uqZfFoZuuEg `zt~? b;t%>WTkķh[2eG8LIWx,^\thrl^Ϊ{=dž<}qV@ ⠨Wy^LF_>0UkDuʫuCs$)Iv:IK;6ֲ4{^6եm+l3>݆uM 9u?>Zc }g~qhKwڭeFMM~pМuqǿz6Tb@8@Y|jx](^]gf}M"tG -w.@vOqh~/HII`S[l.6nØXL9vUcOoB\xoǤ'T&IǍQw_wpv[kmO{w~>#=P1Pɞa-we:iǏlHo׈꒟f9SzH?+shk%Fs:qVhqY`jvO'ρ?PyX3lх]˾uV{ݞ]1,MzYNW~̈́ joYn}ȚF߾׮mS]F z+EDxm/d{F{-W-4wY듏:??_gPf ^3ecg ҵs8R2מz@TANGj)}CNi/R~}c:5{!ZHӋӾ6}T]G]7W6^n 9*,YqOZj:P?Q DFL|?-^.Ɵ7}fFh׶xe2Pscz1&5\cn[=Vn[ĶE鎀uˌd3GII k;lNmشOuuRVfBE]ۣeӶu :X-[(er4~LHi6:Ѻ@ԅrST0trk%$Č0ez" *z"T/X9|8.C5Feg}CQ%͞ˣJvL/?j^h&9xF`њZ(&yF&Iݻfg#W;3^{Wo^4'vV[[K';+mӍִ]AC@W?1^{එyh +^]fm~iԵ]AB@WTk̏t uR?l.OIHiYyԶ]Aˀ7c:q}ힽaf6Z~қm(+sK4{^6}T*UUu]n.:kx{:2 _m=sAߤU@?Z-Vކеz왍Nэ{|5 pڶn b p-@sPg]0G7fy-M{GCF'%{4`=$-Ge\ eU:m+Zt'WjO!OAF@ik&t݆ϥ_ e}=]"Wz_.͜E3leWFih|t-wZۍ-uw=6YN{6|} |*={Ѽn.S.z1zjۻTH]흾 DuDvmvK.`V]yY~sI@t?/ϓ. m&["+P?MzovVЫG3-GRR[(!!\_,^%?v@ҵő m`Y)tem8GMx.))A]Y i`ViW`?^~!S#^+ѽGZj?Vģ0.))A꨷lzL*]OXrY`DBBLOj{-MH'ii-ϰ ok7^ )쭡b]UXSְmռY|5*cֽk0B7镹%ڽP#8nȎq}mJr23_>lE5$iwui+ H~F`IjƵ@q \ @#qG0".0" l`„.0! ,AQHN6qzkKJ#o;`Xv2>,tێJJ7Z/*A .@fفjMzkg @TvZH3Zxu6Ra'%O?/dQ5xYkU]Rֽkق@DaS^RSּ5|BeHNN͘p HvcYcC5:y #`οb;z2.!kr}gUWkyZn=f Pvsn3p~;4p˚=ē~NmI] ¾ 0lH[_L hsh_ғߤc_њec)g7VIZ5yrgk̞W#IjӪv>՞y睝M8[|]\շ8M6%|@PZڨI-m>=k='aiRo-x?>Q.}`Ȏ:Wsmu u > .@,&;+!!˱tﭧDQwRW\vF\~Q7>spYw$%A~;~}6¾ g&if_=j,v+UL1(tWake:@Ș>j$Gq2t7S?vL|]u/ .(0E6Mk6hiۺzښOrifޱxm/Gx> Lal%%~{lBsR4*}{0Z/tNIɚpV^#Lf:u@k#RSu =S^ZyuR/.@n&΃z~B=0eg뺆#,Þ[B/?H uUf7y Wy}Bwegל`Wh(||`l`.;Ws?V@"c:iɍL֯PGv6zctM̠':wuW;d=;EveD}9J@B(0iհ bvP1{\P&G7D޴Iy_$-Qjm~Yrr&]CDv%bh|Yzni_ˆR;kg}nJOIIwyuL}{ЌNj}:+3Y?:WJ/N+Rzd=hb;dj͒suݔ@NKMԄ jqzC5@y°hL m;*5ezᕏ=ep XL n?מ:r`۵tŤZ|1v`V뽧_csج'ߤ%oTuumk%%%h)uy]Nk[n 'b2 l.=͜E%gf$[c;s:V-͞WߤWh-j7]4=F-X]>ZLSi[Y*We;Zan(ӇW|e(HNNP5[= r4tP &0<pc#`vTNV GFqvTi*Tyam$ߏWyE*VJKMTfFw>'$-ؽ.Ho.8c"@DADADADADADADADADA~j*֘,N;Pi3599h=goضLgiJ5փy~}&Zd9p֚ e:|hL``b/d9p? fgg+%%hMgXosج, ΩOl0Zh=xdjLmhݻoO[g_l,8a]٭+ӧ0$I]c]:粹:Teꢢ"5a^Kgh,&= =՟^߶“ߢE ܹS J}I%:8 IDAT~,9/ʃPW'Mo}zNƍ쨓zPbNZ~^z=4mswg;5 Y~SVMRXUյڱRf?s:w ;6H:ºi5-maM&O3;1IKeamZh͛7+##v+c ~u~ca]GnF'ټL~PPPbn voC4R,ӟgg %hq}@#M4IÇ Oy^xMZx ) yOw@HkN˖-Sǎmb]X@n+i͖!++K3gd\$mt$^YfJ\8PRF)77Wא!Cl$i:@@_oG I{$# 8磌ŋ91A (Im7֭>}ߴJq7ޗt^ -[ԩSj*}%]&' -ɓ'ꫯVzzvB#;a 7@GxI{j޼ƌ.LÇWBB7`O"I$/@R @eee@۷>}0,ɒ2$53Xs|cS~rpTYYY} kHc %&k.], @ADADADADADADADADA@lT<%''*Lo^={رc5h %$+CnܸQ3fҥK}vUVVs9G R,_{xˇ3o߾;TTTd}馛]uuuG~iԩ@4bnvmvfϞ /Peeeq}}za I~,誫{UWW뮻}_~YƍSMMMYχ֝waw\ďcxꩧtEƍկ_?۷5@u?1kNׯWzz/wy>}zj3 k(ٺuq_Zvf̘:~ ABQ&r|!%KҥKgԞ={<_X-z !CyFUUz~ ABQIIIjݺW$UXXDٳZ~ ABQƍecW$<(~<RSSvZujjjԧOZQu@4 8m&&&jԩg$ď1h ͟?_{768@g =@`)))5o6m3)ѣƌJ;wҿUTT /KZR{~a=@0o<*狔iFɶ[ˎ;T]]OX@?K.ۈxN pppppppppppppppppPfl߾] ,{ァk۶mڿo5BTӦMӴiӴ|r DB2e|An!Dy'tkΝ[A $***t5' "!駟oaDnΝ:t֭[gDШQ06qD;@ x M6v(PiizmZ4ew"@̴ixf [~-Fٱc&IZ2|n!?$@{[HTɏ#@hȎI# _m(F /6Z3z'\r,r!;w2Z3j=~GY7"I$iI.p_"?pN`y DD?: _  Gÿab7J !Bx@0 Bo cG@`1C[@0G @`0C_u V1 aCX>W ` | `!<S `"<. `#c`?cAC4 ?c p#~@0?:08&_MQ1J h#?/`7;I  q 7a wQ A 1 Hp !#<8/#@1Ul7=S=K.4Z?E_$i@!1!E4?`P_  @Bă10#: "aU,xbFY1 [n|n #'vEH:`xb #vD4Y hi.i&EΖv#O H4IŶ}:Ikh @tZRF#(tXҙzZ ?I3l7q@õ|ۍ1,GpuY Ꮿ@hJv#xxk$ v#9 5 }_$c S#=+"K{F*m7`#%H:NRSp6I?sIՖ{Ap$I$I:QRv2$Z @UJ*$]<FO4IENDB`