1
# Copyright 2010 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
from django.shortcuts import render_to_response
5
from django.conf import settings
6
from django.contrib.admin.views.decorators import staff_member_required
7
from django.utils import simplejson
8
from identityprovider.readonly import (readonly_manager,
10
from django.http import HttpResponse, HttpResponseRedirect, Http404
16
def _remote_req(server, post=None):
17
"""Makes a request to a specific appserver.
19
if post is provided, it should be a sequence of 2-tuples that will be
20
encoded in to POST data.
24
post.append(('secret', settings.READONLY_SECRET))
25
post = urllib.urlencode(post)
26
req = urllib2.Request('http://%s/readonlydata' % server, data=post)
28
oldtimeout = socket.getdefaulttimeout()
29
socket.setdefaulttimeout(5)
30
datafile = urllib2.urlopen(req)
31
data = datafile.read()
32
socket.setdefaulttimeout(oldtimeout)
33
except urllib2.URLError:
38
@staff_member_required
39
def readonly_admin(request):
40
atts = get_server_atts(settings.APP_SERVERS)
41
return render_to_response('admin/readonly.html', atts)
44
def get_server_atts(servers):
45
"""Provides a report about readonly status of all app servers."""
47
set_all_readonly = False
48
clear_all_readonly = False
49
for server in servers:
50
datastr = _remote_req(server)
52
data = {'reachable': False}
55
data = simplejson.loads(datastr)
57
# This is probably caused by the remote database being out
58
# of sync with our current database
59
data = {'reachable': False}
61
data['reachable'] = True
62
if data.get('readonly'):
63
clear_all_readonly = True
65
set_all_readonly = True
67
appservers.append(data)
68
atts = {'appservers': appservers,
69
'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX,
70
'clear_all_readonly': clear_all_readonly,
71
'set_all_readonly': set_all_readonly,
76
@staff_member_required
77
def readonly_confirm(request, action, appserver=None, conn=None):
78
if request.method == 'POST':
79
update_server(action, appserver, conn)
80
return HttpResponseRedirect('/readonly')
81
atts = {'appserver': appserver,
85
return render_to_response('admin/readonly_confirm.html', atts)
88
def update_server(action, appserver=None, conn=None):
90
appservers = settings.APP_SERVERS
92
appservers = [appserver]
93
for server in appservers:
94
post = [('action', action),
97
datastr = _remote_req(server, post)
98
# If we're leaving readonly mode across all appservers,
99
# try to sync up data back from memcached to the DB.
100
if appserver is None and action == 'clear':
101
sync_memcached_to_db()
104
def readonly_data(request):
105
"""Provides data about the readonly status of this app server."""
106
if request.method != 'POST':
108
secret = request.POST.get('secret')
109
if secret != settings.READONLY_SECRET:
111
action = request.POST.get('action')
112
conn = request.POST.get('conn')
114
readonly_manager.set_readonly()
115
elif action == 'clear':
116
readonly_manager.clear_readonly()
117
elif action == 'enable':
118
readonly_manager.clear_failed(conn)
119
elif action == 'disable':
120
readonly_manager.mark_failed(conn)
121
result = {'readonly': readonly_manager.is_readonly()}
123
for conn in readonly_manager.connections:
124
dbid = conn['DATABASE_ID']
125
dbs.append({'id': dbid,
126
'failed': readonly_manager.is_failed(dbid),
128
result['connections'] = dbs
129
return HttpResponse(simplejson.dumps(result))