Re: Freeze break request: make only fas01 clear sessions, resolving a deadlock

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Talked with puiterwijk on line and saw that the fix is basically the
section. It isn't great but a better fix can be done later.

Since this is affecting production items of translations for various
products I am approving this with a +1 and a recommendation of pushing
with retroactive +1 from others as needed.

On 10 August 2015 at 16:38, Patrick Uiterwijk <puiterwijk@xxxxxxxxxx> wrote:
> Change in controllers.py is at line 101: "if socket.gethostname()...."
>
>
>
> commit d0f4e6f6f956133da4116025eead691d4d96fbb7
> Author: Patrick Uiterwijk <puiterwijk@xxxxxxxxxx>
> Date:   Mon Aug 10 22:29:44 2015 +0000
>
>     HOTFIX: Make sure that only fas01clears sessions
>
>     This will prevent deadlocks in the SQL server
>
> diff --git a/roles/fas_server/files/controllers.py b/roles/fas_server/files/controllers.py
> new file mode 100644
> index 0000000..6b15a46
> --- /dev/null
> +++ b/roles/fas_server/files/controllers.py
> @@ -0,0 +1,263 @@
> +# -*- coding: utf-8 -*-
> +#
> +# Copyright © 2008  Ricky Zhou
> +# Copyright © 2008-2014 Red Hat, Inc.
> +#
> +# This copyrighted material is made available to anyone wishing to use, modify,
> +# copy, or redistribute it subject to the terms and conditions of the GNU
> +# General Public License v.2.  This program is distributed in the hope that it
> +# will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
> +# implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> +# See the GNU General Public License for more details.  You should have
> +# received a copy of the GNU General Public License along with this program;
> +# if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
> +# Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
> +# incorporated in the source code or documentation are not subject to the GNU
> +# General Public License and may only be used or replicated with the express
> +# permission of Red Hat, Inc.
> +#
> +# Author(s): Ricky Zhou <ricky@xxxxxxxxxxxxxxxxx>
> +#            Mike McGrath <mmcgrath@xxxxxxxxxx>
> +#            Toshio Kuratomi <toshio@xxxxxxxxxx>
> +#
> +from bunch import Bunch
> +
> +from turbogears import expose, config, identity, redirect
> +from turbogears.database import session
> +from cherrypy import request
> +
> +import turbogears
> +import cherrypy
> +import time
> +
> +from fedora.tg import controllers as f_ctrlers
> +from fedora.tg.utils import request_format
> +
> +from fas import release
> +from fas.user import User
> +from fas.group import Group
> +from fas.configs import Config
> +from fas.fpca import FPCA
> +from fas.json_request import JsonRequest
> +from fas.help import Help
> +from fas.model import Session, People
> +from fas.model import SessionTable
> +
> +
> +from fas.auth import undeprecated_cla_done
> +from fas.util import available_languages
> +
> +from fas import plugin
> +
> +import os
> +
> +import datetime
> +
> +import socket
> +
> +try:
> +    import cPickle as pickle
> +except ImportError:
> +    import pickle
> +
> +class SQLAlchemyStorage:
> +    def __init__(self):
> +        pass
> +
> +    def load(self, session_id):
> +        s = Session.query.get(session_id)
> +        if not s:
> +            return None
> +        expiration_time = s.expiration_time
> +        pickled_data = s.data
> +        data = pickle.loads(pickled_data.encode('utf-8'))
> +        return (data, expiration_time)
> +
> +    # This is an iffy one.  CherryPy's built in session
> +    # storage classes use delete(self, id=None), but it
> +    # isn't called from anywhere in cherrypy.  I think we
> +    # can do this as long as we're careful about how we call it.
> +    def delete(self, session_id=None):
> +        if session_id is None:
> +            session_id = cherrypy.session.id
> +        s = Session.query.get(session_id)
> +        session.delete(s)
> +        session.flush()
> +
> +    def save(self, session_id, data, expiration_time):
> +        pickled_data = pickle.dumps(data)
> +        s = Session.query.get(session_id)
> +        if not s:
> +            s = Session()
> +        s.id = session_id
> +        s.data = pickled_data
> +        s.expiration_time = expiration_time
> +        session.flush()
> +
> +    def acquire_lock(self):
> +        pass
> +
> +    def release_lock(self):
> +        pass
> +
> +    def clean_up(self, sess):
> +        # This is to make sure that only one server cleans up sessions
> +        if socket.gethostname() != 'fas01.phx2.fedoraproject.org':
> +            return
> +        result = SessionTable.delete(
> +            SessionTable.c.expiration_time.__lt__(datetime.datetime.now())
> +            ).execute()
> +
> +config.update({'session_filter.storage_class': SQLAlchemyStorage})
> +
> +def get_locale(locale=None):
> +    if locale:
> +        return locale
> +    try:
> +        return turbogears.identity.current.user.locale
> +    except AttributeError:
> +        pass
> +    try:
> +        return cherrypy.request.simple_cookie['fas_locale'].value
> +    except KeyError:
> +        pass
> +
> +    default_language = config.get('default_language',
> +            turbogears.i18n.utils._get_locale())
> +    return default_language
> +
> +config.update({'i18n.get_locale': get_locale})
> +
> +
> +def add_custom_stdvars(variables):
> +    return variables.update({'gettext': _, "lang": get_locale(),
> +    'available_languages': available_languages(),
> +    'fas_version': release.VERSION,
> +    'webmaster_email': config.get('webmaster_email')})
> +turbogears.view.variable_providers.append(add_custom_stdvars)
> +
> +# from fas import json
> +# import logging
> +# log = logging.getLogger("fas.controllers")
> +
> +#TODO: Appropriate flash icons for errors, etc.
> +# mmcgrath wonders if it will be handy to expose an encrypted mailer with fas
> +# over json for our apps
> +
> +class Root(plugin.RootController):
> +
> +    user = User()
> +    group = Group()
> +    fpca = FPCA()
> +    json = JsonRequest()
> +    config = Config()
> +    help = Help()
> +
> +    def __init__(self):
> +        # TODO: Find a better place for this.
> +        os.environ['GNUPGHOME'] = config.get('gpghome')
> +        plugin.RootController.__init__(self)
> +
> +    def getpluginident(self):
> +        return 'fas'
> +
> +    @expose(template="fas.templates.welcome", allow_json=True)
> +    def index(self):
> +        if turbogears.identity.not_anonymous():
> +            if request_format() == 'json':
> +                # redirects don't work with JSON calls.  This is a bit of a
> +                # hack until we can figure out something better.
> +                return dict()
> +            turbogears.redirect('/home')
> +        return dict(now=time.ctime())
> +
> +    @identity.require(identity.not_anonymous())
> +    @expose(template="fas.templates.home", allow_json=True)
> +    def home(self):
> +        user_name = turbogears.identity.current.user_name
> +        person = People.by_username(user_name)
> +        (cla_done, undeprecated_cla) = undeprecated_cla_done(person)
> +
> +        person = person.filter_private()
> +        return dict(person=person, memberships=person['memberships'], cla=undeprecated_cla)
> +
> +    @expose(template="fas.templates.about")
> +    def about(self):
> +        return dict()
> +
> +    @expose(template="fas.templates.login", allow_json=True)
> +    def login(self, forward_url=None, *args, **kwargs):
> +        '''Page to become authenticated to the Account System.
> +
> +        This shows a small login box to type in your username and password
> +        from the Fedora Account System.
> +
> +        :kwarg forward_url: The url to send to once authentication succeeds
> +        '''
> +        actual_login_dict = f_ctrlers.login(forward_url=forward_url, *args, **kwargs)
> +
> +        try:
> +            login_dict = Bunch()
> +            login_dict['user'] = Bunch()
> +            for field in People.allow_fields['complete']:
> +                login_dict['user'][field] = None
> +            for field in People.allow_fields['self']:
> +                login_dict['user'][field] = getattr(actual_login_dict['user'], field)
> +            # Strip out things that the user shouldn't see about their own
> +            # login
> +            login_dict['user']['internal_comments'] = None
> +            login_dict['user']['emailtoken'] = None
> +            login_dict['user']['security_answer'] = None
> +            login_dict['user']['alias_enabled'] = None
> +            login_dict['user']['passwordtoken'] = None
> +
> +            # Add things that are needed by some other apps
> +            login_dict['user'].approved_memberships = list(
> +                    actual_login_dict['user'].approved_memberships)
> +            login_dict['user'].memberships = list(actual_login_dict['user'].memberships)
> +            login_dict['user'].unapproved_memberships = list(
> +                    actual_login_dict['user'].unapproved_memberships)
> +            login_dict['user'].group_roles = list(actual_login_dict['user'].group_roles)
> +            login_dict['user'].roles = list(actual_login_dict['user'].roles)
> +            login_dict['user'].groups = [g.name for g in actual_login_dict['user'].approved_memberships]
> +            return login_dict
> +        except KeyError, e:
> +            # No problem, this usually means that we failed to login and
> +            # therefore we don't have a user field.
> +            login_dict = actual_login_dict
> +
> +        if not identity.current.anonymous and identity.was_login_attempted() \
> +                and not identity.get_identity_errors():
> +            # Success that needs to be passed back via json
> +            return login_dict
> +
> +        if identity.was_login_attempted() and request.fas_provided_username:
> +            if request.fas_identity_failure_reason == 'status_inactive':
> +                turbogears.flash(_('Your old password has expired.  Please'
> +                    ' reset your password below.'))
> +                if request_format() != 'json':
> +                    redirect('/user/resetpass')
> +            if request.fas_identity_failure_reason == 'status_account_disabled':
> +                turbogears.flash(_('Your account is currently disabled.  For'
> +                        ' more information, please contact %(admin_email)s' %
> +                        {'admin_email': config.get('accounts_email')}))
> +                if request_format() != 'json':
> +                    redirect('/login')
> +
> +        return login_dict
> +
> +    @expose(allow_json=True)
> +    def logout(self):
> +        return f_ctrlers.logout()
> +
> +    @expose()
> +    def language(self, locale):
> +        if locale not in available_languages():
> +            turbogears.flash(_('The language \'%s\' is not available.') % locale)
> +            redirect(request.headers.get("Referer", "/"))
> +            return dict()
> +        #turbogears.i18n.set_session_locale(locale)
> +        cherrypy.response.simple_cookie['fas_locale'] = locale
> +        redirect(request.headers.get("Referer", "/"))
> +        return dict()
> +
> diff --git a/roles/fas_server/tasks/main.yml b/roles/fas_server/tasks/main.yml
> index 57370a8..980013d 100644
> --- a/roles/fas_server/tasks/main.yml
> +++ b/roles/fas_server/tasks/main.yml
> @@ -355,3 +355,12 @@
>    - config
>    - fas
>    - hotfixfas
> +
> +- name: HOTFIX make sure only fas01 cleans up sessions
> +  copy: src={{ roles }}/fas_server/files/controllers.py
> +        dest=/usr/lib/python2.6/site-packages/fas/controllers.py
> +        mode=644 owner=root group=root
> +  tags:
> +  - config
> +  - fas
> +  - hotfixfas



-- 
Stephen J Smoogen.




[Index of Archives]     [Fedora Development]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]

  Powered by Linux