1
|
|
|
# vim: set fileencoding=utf-8 : |
2
|
|
|
from functools import update_wrapper |
3
|
|
|
|
4
|
|
|
from django.contrib import admin |
5
|
|
|
|
6
|
|
|
try: |
7
|
|
|
from django.urls import reverse |
8
|
|
|
except: |
9
|
|
|
from django.core.urlresolvers import reverse |
10
|
|
|
|
11
|
|
|
from django.http import HttpResponseForbidden |
12
|
|
|
from django.shortcuts import get_object_or_404 |
13
|
|
|
from django.utils.translation import ugettext_lazy as _ |
14
|
|
|
|
15
|
|
|
from .models import RedisServer |
16
|
|
|
from .utils import PY3 |
17
|
|
|
from .views import inspect |
18
|
|
|
|
19
|
|
|
if PY3: |
20
|
|
|
unicode = str |
21
|
|
|
|
22
|
|
|
|
23
|
|
|
class RedisServerAdmin(admin.ModelAdmin): |
24
|
|
|
class Media: |
25
|
|
|
css = { |
26
|
|
|
'all': ('redisboard/admin.css',) |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
list_display = ( |
30
|
|
|
'__unicode__', |
31
|
|
|
'status', |
32
|
|
|
'memory', |
33
|
|
|
'clients', |
34
|
|
|
'details', |
35
|
|
|
'cpu_utilization', |
36
|
|
|
'slowlog', |
37
|
|
|
'tools', |
38
|
|
|
) |
39
|
|
|
|
40
|
|
|
list_filter = 'label', 'hostname' |
41
|
|
|
ordering = ('hostname', 'port') |
42
|
|
|
|
43
|
|
|
def slowlog(self, obj): |
44
|
|
|
output = [(float('inf'), 'Total: %d items' % obj.stats['slowlog_len'])] |
45
|
|
|
for log in obj.stats['slowlog']: |
46
|
|
|
command = log['command'] |
47
|
|
|
|
48
|
|
|
if len(command) > 255: |
49
|
|
|
command = str(command[:252]) + '...' |
50
|
|
|
|
51
|
|
|
output.append(( |
52
|
|
|
log['duration'], |
53
|
|
|
u'%.1fms: %r' % (log['duration'] / 1000., command), |
54
|
|
|
)) |
55
|
|
|
if output: |
56
|
|
|
return '<br>'.join(l for _, l in sorted(output, reverse=True)) |
57
|
|
|
else: |
58
|
|
|
return 'n/a' |
59
|
|
|
slowlog.allow_tags = True |
60
|
|
|
slowlog.long_description = _('Slowlog') |
61
|
|
|
|
62
|
|
|
def status(self, obj): |
63
|
|
|
return obj.stats['status'] |
64
|
|
|
status.long_description = _("Status") |
65
|
|
|
|
66
|
|
|
def memory(self, obj): |
67
|
|
|
return obj.stats['memory'] |
68
|
|
|
memory.long_description = _("Memory") |
69
|
|
|
|
70
|
|
|
def clients(self, obj): |
71
|
|
|
return obj.stats['clients'] |
72
|
|
|
clients.long_description = _("Clients") |
73
|
|
|
|
74
|
|
|
def tools(self, obj): |
75
|
|
|
return '<a href="%s">%s</a>' % ( |
76
|
|
|
reverse("admin:redisboard_redisserver_inspect", args=(obj.id,)), |
77
|
|
|
unicode(_("Inspect")) |
78
|
|
|
) |
79
|
|
|
tools.allow_tags = True |
80
|
|
|
tools.long_description = _("Tools") |
81
|
|
|
|
82
|
|
|
def details(self, obj): |
83
|
|
|
output = [] |
84
|
|
|
brief_details = obj.stats['brief_details'] |
85
|
|
|
for k, v in (brief_details.items() if PY3 else brief_details.iteritems()): |
86
|
|
|
output.append('<dt>%s</dt><dd>%s</dd>' % (k, v)) |
87
|
|
|
if output: |
88
|
|
|
return '<dl class="details">%s</dl>' % ''.join(output) |
89
|
|
|
return 'n/a' |
90
|
|
|
details.allow_tags = True |
91
|
|
|
details.long_description = _("Details") |
92
|
|
|
|
93
|
|
|
def cpu_utilization(self, obj): |
94
|
|
|
stats = obj.stats |
95
|
|
|
if stats['status'] != 'UP': |
96
|
|
|
return 'n/a' |
97
|
|
|
|
98
|
|
|
data = ( |
99
|
|
|
'used_cpu_sys', |
100
|
|
|
'used_cpu_sys_children', |
101
|
|
|
'used_cpu_user', |
102
|
|
|
'used_cpu_user_children', |
103
|
|
|
) |
104
|
|
|
data = dict((k, stats['details'][k]) for k in data) |
105
|
|
|
total_cpu = sum(data.values() if PY3 else data.itervalues()) |
106
|
|
|
uptime = stats['details']['uptime_in_seconds'] |
107
|
|
|
data['cpu_utilization'] = '%.3f%%' % (total_cpu / uptime if uptime else 0) |
108
|
|
|
|
109
|
|
|
data = sorted(data.items()) |
110
|
|
|
|
111
|
|
|
output = [] |
112
|
|
|
for k, v in data: |
113
|
|
|
k = k.replace('_', ' ') |
114
|
|
|
output.append('<dt>%s</dt><dd>%s</dd>' % (k, v)) |
115
|
|
|
|
116
|
|
|
return '<dl class="details">%s</dl>' % ''.join(output) |
117
|
|
|
cpu_utilization.allow_tags = True |
118
|
|
|
cpu_utilization.long_description = _('CPU Utilization') |
119
|
|
|
|
120
|
|
|
def get_urls(self): |
121
|
|
|
urlpatterns = super(RedisServerAdmin, self).get_urls() |
122
|
|
|
try: |
123
|
|
|
from django.conf.urls import url |
124
|
|
|
except ImportError: |
125
|
|
|
from django.conf.urls.defaults import url |
126
|
|
|
|
127
|
|
|
def wrap(view): |
128
|
|
|
def wrapper(*args, **kwargs): |
129
|
|
|
return self.admin_site.admin_view(view)(*args, **kwargs) |
130
|
|
|
|
131
|
|
|
return update_wrapper(wrapper, view) |
132
|
|
|
|
133
|
|
|
return [url(r'^(\d+)/inspect/$', |
134
|
|
|
wrap(self.inspect_view), |
135
|
|
|
name='redisboard_redisserver_inspect') |
136
|
|
|
] + urlpatterns |
137
|
|
|
|
138
|
|
|
def inspect_view(self, request, server_id): |
139
|
|
|
server = get_object_or_404(RedisServer, id=server_id) |
140
|
|
|
if ( |
141
|
|
|
self.has_change_permission(request, server) and |
142
|
|
|
request.user.has_perm('redisboard.can_inspect') |
143
|
|
|
): |
144
|
|
|
return inspect(request, server) |
145
|
|
|
else: |
146
|
|
|
return HttpResponseForbidden("You can't inspect this server.") |
147
|
|
|
|
148
|
|
|
|
149
|
|
|
admin.site.register(RedisServer, RedisServerAdmin) |
150
|
|
|
|