Skip to content
This repository was archived by the owner on Jan 8, 2020. It is now read-only.

Commit 9c8ed18

Browse files
author
jjmleiro
committedApr 7, 2015
Merge branch 'release/2.0.0'
·
4.1.12.0.0
2 parents 122d273 + b231f7c commit 9c8ed18

26 files changed

+3439
-823
lines changed
 

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ local_settings.py
1111
__MACOSX
1212
.DS_Store
1313

14+
.pydevproject
1415
.project
1516
.settings
1617
.classpath

‎AUTHORS‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Daniel Tardon <dtardon@redoop.org>
1+
Daniel Tardon <dtardon@keedio.org>

‎CHANGES.rst‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
Changelog
22
=========
33

4+
2.0.0 (20-02-2015)
5+
----------------
6+
7+
Features
8+
********
9+
10+
- Zookeeper REST server API usage.
11+
- Custom Dashboard based on Kafka JMX metrics published in Ganglia
12+
13+
Bug Handling
14+
************
15+
- Issue #18: Justify Right for "Search" DataTable Box
16+
- Issue #21: Show X-Axis Legend
17+
- Issue #23: Header don't show name correctly
18+
- Issue #24: Block submit button while not show graphics in dashboard.
19+
- Issue #25: Fix internalization messages
20+
- Issue #26: Format/Size in Y-Axis in dashboard
21+
- Issue #28: Error in "FifteenMinuteRate Graph" in date format
22+
- Issue #29: Error in dashboard when Ganglia URL is not correct.
23+
- Issue #30: In dashboard, filter "All topics" not working correctly
24+
- Issue #32: Fix compile locales
25+
- Issue #33: Ganglia URL incorrect
26+
- Issue #34: Tests - Fix compile locales
27+
- Issue #38: Include name of Ganglia Cluster in Hue.ini
28+
29+
430
1.0 (14-04-2014)
531
----------------
632

‎README.md‎

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ Kafka-HUE: Apache Kafka HUE Application
33

44
Kafka-HUE is a [HUE](http://www.gethue.com) application to admin and manage a pool of [Apache Kafka](http://kafka.apache.org/) clusters.
55

6+
Features
7+
--------
8+
* Multi cluster support
9+
* Cluster Overview (Brokers, Topics, Consumers, etc.)
10+
* Custom Dashboards based on Ganglia metrics. Configure Kafka to export JMX metrics to Ganglia and Kafka-HUE will be able to show all of those metrics specifiying them in a config file (metrics.ini).
11+
612
Requirements
713
------------
8-
- [HUE 3.5.0](http://www.gethue.com)
9-
- [Kazoo 1.3.1](http://github.com/python-zk/kazoo)
10-
- [Zope Interface -4.1.1](http://pypi.python.org/pypi/zope.interface/4.1.1)
14+
- [HUE 3.7.1](http://www.gethue.com)
15+
- [Ganglia](http://ganglia.sourceforge.net/)
16+
- Zookeeper REST
1117

1218
Main Stack
1319
----------
@@ -21,9 +27,7 @@ Installation
2127
------------
2228
To get the Kafka-HUE app integrated and running in your HUE deployment:
2329

24-
$ sudo $HUE_HOME/build/env/bin/python $HUE_HOME/build/env/bin/pip install zope.interface
25-
$ sudo $HUE_HOME/build/env/bin/python $HUE_HOME/build/env/bin/pip install kazoo
26-
$ git clone http://github.com/danieltardon/kafka-hue.git
30+
$ git clone https://github.com/keedio/kafka-hue.git
2731
$ mv kafka-hue/kafka $HUE_HOME/apps
2832
$ cd $HUE_HOME/apps
2933
$ sudo ../tools/app_reg/app_reg.py --install kafka --relative-paths
@@ -52,6 +56,35 @@ Configs needed in hue.ini config file.
5256
# Path to consumers info in Zookeeper Znode hierarchy
5357
consumers_path=/consumers
5458

59+
# Ganglia Server
60+
# e.g. http://localhost
61+
ganglia_server=http://localhost
62+
63+
# Ganglia Data Source
64+
# e.g. GangliaCluster
65+
ganglia_data_source=GangliaCluster
66+
67+
68+
Metrics.ini Config file
69+
-----------------------
70+
Metrics example
71+
72+
[BrokerTopicMetrics.BytesInPerSec]
73+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
74+
75+
[BrokerTopicMetrics.BytesOutPerSec]
76+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
77+
78+
[BrokerTopicMetrics.FailedFetchRequestPerSec]
79+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
80+
81+
[BrokerTopicMetrics.FailedProduceRequestPerSec]
82+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
83+
84+
[BrokerTopicMetrics.MessagesInPerSec]
85+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
86+
87+
5588
Compile locales
5689
---------------
5790
To compile the locales:
@@ -71,4 +104,4 @@ Apache License, Version 2.0
71104
http://www.apache.org/licenses/LICENSE-2.0
72105

73106
--
74-
Daniel Tardón <dtardon@redoop.org>
107+
Daniel Tardón <dtardon@keedio.org>

‎kafka/Makefile‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Licensed to Cloudera, Inc. under one
1+
# Licensed to the Apache Software Foundation (ASF) under one
22
# or more contributor license agreements. See the NOTICE file
33
# distributed with this work for additional information
4-
# regarding copyright ownership. Cloudera, Inc. licenses this file
4+
# regarding copyright ownership. The ASF licenses this file
55
# to you under the Apache License, Version 2.0 (the
66
# "License"); you may not use this file except in compliance
77
# with the License. You may obtain a copy of the License at
88
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
9+
# http:# www.apache.org/licenses/LICENSE-2.0
1010
#
1111
# Unless required by applicable law or agreed to in writing, software
1212
# distributed under the License is distributed on an "AS IS" BASIS,

‎kafka/setup.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Licensed to Cloudera, Inc. under one
1+
# Licensed to the Apache Software Foundation (ASF) under one
22
# or more contributor license agreements. See the NOTICE file
33
# distributed with this work for additional information
4-
# regarding copyright ownership. Cloudera, Inc. licenses this file
4+
# regarding copyright ownership. The ASF licenses this file
55
# to you under the Apache License, Version 2.0 (the
66
# "License"); you may not use this file except in compliance
77
# with the License. You may obtain a copy of the License at
88
#
9-
# http://www.apache.org/licenses/LICENSE-2.0
9+
# http:# www.apache.org/licenses/LICENSE-2.0
1010
#
1111
# Unless required by applicable law or agreed to in writing, software
1212
# distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,10 +27,10 @@ def expand_package_data(src_dirs, strip=""):
2727
os.chdir(os.path.dirname(os.path.abspath(__file__)))
2828
setup(
2929
name = "kafka",
30-
version = "1.0",
31-
url = 'www.redoop.org',
30+
version = "2.0.0",
31+
url = 'www.keedio.com',
3232
description = 'Kafka HUE integration',
33-
author = 'dtardon',
33+
author = 'dtardon@keedio.com',
3434
packages = find_packages('src'),
3535
package_dir = {'': 'src'},
3636
install_requires = ['setuptools', 'desktop'],

‎kafka/src/kafka/conf.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def coerce_string(value):
2222
else:
2323
return value
2424

25+
2526
CLUSTERS = UnspecifiedConfigSection(
2627
"clusters",
2728
help="One entry for each Zookeeper cluster",
@@ -58,6 +59,16 @@ def coerce_string(value):
5859
default="/brokers/topics",
5960
type=str,
6061
),
62+
GANGLIA_SERVER = Config( "ganglia_server",
63+
help="GANGLIA Server",
64+
default="http://localhost",
65+
type=str,
66+
),
67+
GANGLIA_DATA_SOURCE = Config( "ganglia_data_source",
68+
help="Ganglia Data Source",
69+
default="my cluster",
70+
type=str,
71+
),
6172
)
6273
)
6374
)

‎kafka/src/kafka/locale/en/LC_MESSAGES/django.po‎

Lines changed: 311 additions & 122 deletions
Large diffs are not rendered by default.

‎kafka/src/kafka/locale/en_US.pot‎

Lines changed: 313 additions & 124 deletions
Large diffs are not rendered by default.

‎kafka/src/kafka/locale/es/LC_MESSAGES/django.po‎

Lines changed: 316 additions & 127 deletions
Large diffs are not rendered by default.

‎kafka/src/kafka/metrics.ini‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[BrokerTopicMetrics.BytesInPerSec]
2+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
3+
4+
[BrokerTopicMetrics.BytesOutPerSec]
5+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
6+
7+
[BrokerTopicMetrics.FailedFetchRequestPerSec]
8+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
9+
10+
[BrokerTopicMetrics.FailedProduceRequestPerSec]
11+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate
12+
13+
[BrokerTopicMetrics.MessagesInPerSec]
14+
key = Count,OneMinuteRate,FiveMinuteRate,FifteenMinuteRate,MeanRate

‎kafka/src/kafka/rest.py‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env python
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http:# www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
import urllib2
19+
import simplejson
20+
21+
22+
class ZooKeeper(object):
23+
""" Zoookeeper Class """
24+
25+
class Error(Exception):
26+
pass
27+
28+
class NotFound(Error):
29+
pass
30+
31+
class RESTError(Error):
32+
pass
33+
34+
def __init__(self, uri='http://localhost:9998'):
35+
self._base = uri
36+
try:
37+
self.get("/")
38+
except ZooKeeper.NotFound:
39+
raise ZooKeeper.RESTError
40+
41+
def get(self, path):
42+
""" Get a node """
43+
url = "%s/znodes/v1%s" % (self._base, path)
44+
return self._do_get(url)
45+
46+
def get_children(self, path):
47+
""" Get all the children for a given path. This function creates a generator """
48+
for child_path in self.get_children_paths(path, uris=True):
49+
try:
50+
yield self._do_get(child_path)
51+
except ZooKeeper.NotFound:
52+
continue
53+
54+
def get_children_paths(self, path, uris=False):
55+
""" Get the paths for children nodes """
56+
url = "%s/znodes/v1%s?view=children" % (self._base, path)
57+
try:
58+
resp = self._do_get(url)
59+
for child in resp.get('children', []):
60+
yield child if not uris else resp['child_uri_template']\
61+
.replace('{child}', urllib2.quote(child))
62+
63+
except ZooKeeper.NotFound:
64+
raise
65+
66+
67+
def _do_get(self, uri):
68+
""" Send a GET request and convert errors to exceptions """
69+
try:
70+
req = urllib2.urlopen(uri)
71+
resp = simplejson.load(req)
72+
73+
if 'Error' in resp:
74+
raise ZooKeeper.Error(resp['Error'])
75+
76+
return resp
77+
except urllib2.URLError:
78+
raise ZooKeeper.NotFound(uri)
79+
80+
except urllib2.HTTPError, e:
81+
if e.code == 404:
82+
raise ZooKeeper.NotFound(uri)
83+
raise
84+

‎kafka/src/kafka/settings.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,14 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
import os
18+
1719
DJANGO_APPS = [ "kafka" ]
1820
REQUIRES_HADOOP = False
1921
MENU_INDEX = 100
2022
ICON = "/kafka/static/art/icon_kafka_24.png"
2123
IS_URL_NAMESPACED = True
2224

25+
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__))
26+
METRICS_INI = os.path.join(PROJECT_ROOT, 'metrics.ini')
27+
22.9 KB
Loading

‎kafka/src/kafka/static/css/kafka.css‎

Lines changed: 308 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,311 @@
1818
float: right;
1919
text-align: right;
2020
vertical-align:top;
21-
}
21+
}
22+
23+
textarea {
24+
resize: none;
25+
}
26+
27+
.text-left {
28+
text-align: left;
29+
}
30+
.text-right {
31+
text-align: right;
32+
}
33+
.text-center {
34+
text-align: center;
35+
}
36+
.text-justify {
37+
text-align: justify;
38+
}
39+
40+
.pull-right {
41+
float: right !important;
42+
}
43+
.pull-left {
44+
float: left !important;
45+
}
46+
47+
.show-grid [class^=col-] {
48+
padding-top: 10px;
49+
padding-bottom: 10px;
50+
border: 1px solid #ddd;
51+
background-color: #eee!important;
52+
}
53+
54+
.huge {
55+
font-size: 20px;
56+
}
57+
.panel-green {
58+
border-color: #5cb85c;
59+
}
60+
61+
.panel-green .panel-heading {
62+
border-color: #5cb85c;
63+
color: #fff;
64+
background-color: #5cb85c;
65+
}
66+
67+
.panel-green a {
68+
color: #5cb85c;
69+
}
70+
71+
.panel-green a:hover {
72+
color: #3d8b3d;
73+
}
74+
75+
.panel-red {
76+
border-color: #d9534f;
77+
}
78+
79+
.panel-red .panel-heading {
80+
border-color: #d9534f;
81+
color: #fff;
82+
background-color: #d9534f;
83+
}
84+
85+
.panel-red a {
86+
color: #d9534f;
87+
}
88+
89+
.panel-red a:hover {
90+
color: #b52b27;
91+
}
92+
93+
.panel {
94+
margin-bottom: 20px;
95+
background-color: #fff;
96+
border: 1px solid transparent;
97+
border-radius: 4px;
98+
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
99+
box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
100+
}
101+
.panel-body {
102+
padding: 15px;
103+
}
104+
.panel-heading {
105+
padding: 10px 15px;
106+
border-bottom: 1px solid transparent;
107+
border-top-left-radius: 3px;
108+
border-top-right-radius: 3px;
109+
}
110+
.panel-heading > .dropdown .dropdown-toggle {
111+
color: inherit;
112+
}
113+
.panel-title {
114+
margin-top: 0;
115+
margin-bottom: 0;
116+
font-size: 16px;
117+
color: inherit;
118+
}
119+
.panel-title > a {
120+
color: inherit;
121+
}
122+
.panel-footer {
123+
padding: 10px 15px;
124+
background-color: #CFD1D2;
125+
border-top: 1px solid #ddd;
126+
border-bottom-right-radius: 3px;
127+
border-bottom-left-radius: 3px;
128+
}
129+
.panel > .list-group,
130+
.panel > .panel-collapse > .list-group {
131+
margin-bottom: 0;
132+
}
133+
.panel > .list-group .list-group-item,
134+
.panel > .panel-collapse > .list-group .list-group-item {
135+
border-width: 1px 0;
136+
border-radius: 0;
137+
}
138+
.panel > .list-group:first-child .list-group-item:first-child,
139+
.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
140+
border-top: 0;
141+
border-top-left-radius: 3px;
142+
border-top-right-radius: 3px;
143+
}
144+
.panel > .list-group:last-child .list-group-item:last-child,
145+
.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
146+
border-bottom: 0;
147+
border-bottom-right-radius: 3px;
148+
border-bottom-left-radius: 3px;
149+
}
150+
.panel-heading + .list-group .list-group-item:first-child {
151+
border-top-width: 0;
152+
}
153+
.list-group + .panel-footer {
154+
border-top-width: 0;
155+
}
156+
157+
.panel-group {
158+
margin-bottom: 20px;
159+
}
160+
.panel-group .panel {
161+
margin-bottom: 0;
162+
border-radius: 4px;
163+
}
164+
.panel-group .panel + .panel {
165+
margin-top: 5px;
166+
}
167+
.panel-group .panel-heading {
168+
border-bottom: 0;
169+
}
170+
.panel-group .panel-heading + .panel-collapse > .panel-body,
171+
.panel-group .panel-heading + .panel-collapse > .list-group {
172+
border-top: 1px solid #ddd;
173+
}
174+
.panel-group .panel-footer {
175+
border-top: 0;
176+
}
177+
.panel-group .panel-footer + .panel-collapse .panel-body {
178+
border-bottom: 1px solid #ddd;
179+
}
180+
.panel-default {
181+
border-color: #ddd;
182+
}
183+
.panel-default > .panel-heading {
184+
color: #333;
185+
background-color: #CFD1D2;
186+
border-color: #ddd;
187+
}
188+
.panel-default > .panel-heading + .panel-collapse > .panel-body {
189+
border-top-color: #ddd;
190+
}
191+
.panel-default > .panel-heading .badge {
192+
color: #CFD1D2;
193+
background-color: #333;
194+
}
195+
.panel-default > .panel-footer + .panel-collapse > .panel-body {
196+
border-bottom-color: #ddd;
197+
}
198+
.panel-primary {
199+
border-color: #428bca;
200+
}
201+
.panel-primary > .panel-heading {
202+
color: #fff;
203+
background-color: #428bca;
204+
border-color: #428bca;
205+
}
206+
.panel-primary > .panel-heading + .panel-collapse > .panel-body {
207+
border-top-color: #428bca;
208+
}
209+
.panel-primary > .panel-heading .badge {
210+
color: #428bca;
211+
background-color: #fff;
212+
}
213+
.panel-primary > .panel-footer + .panel-collapse > .panel-body {
214+
border-bottom-color: #428bca;
215+
}
216+
.panel-success {
217+
border-color: #d6e9c6;
218+
}
219+
.panel-success > .panel-heading {
220+
color: #3c763d;
221+
background-color: #dff0d8;
222+
border-color: #d6e9c6;
223+
}
224+
.panel-success > .panel-heading + .panel-collapse > .panel-body {
225+
border-top-color: #d6e9c6;
226+
}
227+
.panel-success > .panel-heading .badge {
228+
color: #dff0d8;
229+
background-color: #3c763d;
230+
}
231+
.panel-success > .panel-footer + .panel-collapse > .panel-body {
232+
border-bottom-color: #d6e9c6;
233+
}
234+
.panel-info {
235+
border-color: #bce8f1;
236+
}
237+
.panel-info > .panel-heading {
238+
color: #31708f;
239+
background-color: #d9edf7;
240+
border-color: #bce8f1;
241+
}
242+
.panel-info > .panel-heading + .panel-collapse > .panel-body {
243+
border-top-color: #bce8f1;
244+
}
245+
.panel-info > .panel-heading .badge {
246+
color: #d9edf7;
247+
background-color: #31708f;
248+
}
249+
.panel-info > .panel-footer + .panel-collapse > .panel-body {
250+
border-bottom-color: #bce8f1;
251+
}
252+
.panel-warning {
253+
border-color: #faebcc;
254+
}
255+
.panel-warning > .panel-heading {
256+
color: #8a6d3b;
257+
background-color: #fcf8e3;
258+
border-color: #faebcc;
259+
}
260+
.panel-warning > .panel-heading + .panel-collapse > .panel-body {
261+
border-top-color: #faebcc;
262+
}
263+
.panel-warning > .panel-heading .badge {
264+
color: #fcf8e3;
265+
background-color: #8a6d3b;
266+
}
267+
.panel-warning > .panel-footer + .panel-collapse > .panel-body {
268+
border-bottom-color: #faebcc;
269+
}
270+
.panel-danger {
271+
border-color: #ebccd1;
272+
}
273+
.panel-danger > .panel-heading {
274+
color: #a94442;
275+
background-color: #f2dede;
276+
border-color: #ebccd1;
277+
}
278+
.panel-danger > .panel-heading + .panel-collapse > .panel-body {
279+
border-top-color: #ebccd1;
280+
}
281+
.panel-danger > .panel-heading .badge {
282+
color: #f2dede;
283+
background-color: #a94442;
284+
}
285+
.panel-danger > .panel-footer + .panel-collapse > .panel-body {
286+
border-bottom-color: #ebccd1;
287+
}
288+
289+
.col-xs-3, .col-lg-4, .col-xs-9 {
290+
position: relative;
291+
min-height: 1px;
292+
padding-right: 15px;
293+
padding-left: 15px;
294+
}
295+
.col-xs-3, .col-xs-9 {
296+
float: left;
297+
}
298+
299+
.col-xs-pull-9 {
300+
right: 75%;
301+
}
302+
.col-xs-pull-3 {
303+
right: 25%;
304+
}
305+
.col-xs-push-9 {
306+
left: 75%;
307+
}
308+
.col-xs-push-3 {
309+
left: 25%;
310+
}
311+
.col-xs-offset-9 {
312+
margin-left: 75%;
313+
}
314+
.col-xs-offset-3 {
315+
margin-left: 25%;
316+
}
317+
.row:before,
318+
.row:after,
319+
.media-right,
320+
.media > .pull-right {
321+
padding-left: 10px;
322+
}
323+
.media-left,
324+
.media > .pull-left {
325+
padding-right: 10px;
326+
}
327+
.media-left,
328+
.media-right,

‎kafka/src/kafka/static/js/jquery.smart_autocomplete.js‎

Lines changed: 545 additions & 0 deletions
Large diffs are not rendered by default.

‎kafka/src/kafka/templates/cluster.mako‎

Lines changed: 80 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ ${commonheader("Kafka > %s" % (cluster['cluster']['nice_name']), app_name, user)
3838
"bLengthChange":true,
3939
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
4040
"oLanguage":{
41+
"sLengthMenu":"${_('Show _MENU_ entries')}",
42+
"sSearch":"${_('Search')}",
4143
"sEmptyTable":"${_('No data available')}",
4244
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
4345
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -58,6 +60,8 @@ ${commonheader("Kafka > %s" % (cluster['cluster']['nice_name']), app_name, user)
5860
"bLengthChange":true,
5961
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
6062
"oLanguage":{
63+
"sLengthMenu":"${_('Show _MENU_ entries')}",
64+
"sSearch":"${_('Search')}",
6165
"sEmptyTable":"${_('No data available')}",
6266
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
6367
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -105,8 +109,14 @@ ${ kafka.menubar(section='Topology',c_id=cluster['cluster']['id']) }
105109

106110
<h2 class="card-heading simple">${ _('Topology of Kakfa cluster:') } ${ cluster['cluster']['nice_name'] }</h2>
107111
<div class="card-body">
108-
<div class="alert alert-info">${ _('The zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b></div>
109-
112+
% if cluster['error'] == 0 :
113+
<div class="alert alert-info">${ _('The zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b></div>
114+
% else:
115+
<div class="alert alert-error">
116+
${ _('Error connecting to the zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b><br>
117+
${ _('Please contact your administrator to solve this.') }
118+
</div>
119+
% endif
110120
<h4 class="card-heading simple">${ _('Zookepers') }</h4>
111121
</br>
112122
<table class="table table-hover table-striped table-condensed">
@@ -137,62 +147,75 @@ ${ kafka.menubar(section='Topology',c_id=cluster['cluster']['id']) }
137147
</tbody>
138148
</table>
139149
</br>
140-
<h4 class="card-heading simple">${ _('Brokers') }</h4>
141-
</br>
142-
<table class="table datatables table-striped table-hover table-condensed" id="brokersTable" data-tablescroller-disable="true">
143-
<thead>
144-
<tr>
145-
<th>${ _('Broker ID') }</th>
146-
<th>${ _('Hostname') }</th>
147-
<th>${ _('Port') }</th>
148-
<th>${ _('Status') }</th>
149-
</tr>
150-
</thead>
151-
<tbody>
152-
% for broker in cluster['brokers']:
153-
<tr>
154-
<td>${broker['id']}</td>
155-
<td>${broker['host']}</td>
156-
<td>${broker['port']}</td>
157-
<%
158-
error = test_connection(broker['host'],broker['port'])
159-
%>
160-
<td>
161-
% if not error:
162-
<span class="label label-success">${ _('ONLINE') }</span>
163-
% else:
164-
<span class="label label-warning">${ _('OFFLINE') }</span>
165-
% endif
166-
</td>
167-
</tr>
168-
% endfor
169-
</tbody>
170-
</table>
171-
</br>
172-
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
173-
</br>
174-
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
175-
<thead>
176-
<tr>
177-
<th>${ _('Name') }</th>
178-
<th>${ _('Status') }</th>
179-
</tr>
180-
</thead>
181-
<tbody>
182-
% for consumer in cluster['consumer_groups']:
183-
<tr>
184-
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['cluster']['id'], group_id=consumer)}">${consumer}</a></td>
185-
<td>
186-
% if cluster['consumer_groups_status'][consumer] == 0:
187-
<span class="label label-warning">${ _('OFFLINE') }</span>
188-
% else:
189-
<span class="label label-success">${ _('ONLINE') }</span>
190-
% endif
191-
</td>
192-
</tr>
150+
% if cluster['error'] == 0 :
151+
<h4 class="card-heading simple">${ _('Brokers') }</h4>
152+
</br>
153+
<table class="table datatables table-striped table-hover table-condensed" id="brokersTable" data-tablescroller-disable="true">
154+
<thead>
155+
<tr>
156+
<th>${ _('Broker ID') }</th>
157+
<th>${ _('Hostname') }</th>
158+
<th>${ _('Port') }</th>
159+
<th>${ _('Status') }</th>
160+
</tr>
161+
</thead>
162+
<tbody>
163+
% for broker in cluster['brokers']:
164+
<tr>
165+
<td>${broker['id']}</td>
166+
<td>${broker['host']}</td>
167+
<td>${broker['port']}</td>
168+
<%
169+
error = test_connection(broker['host'],broker['port'])
170+
%>
171+
<td>
172+
% if not error:
173+
<span class="label label-success">${ _('ONLINE') }</span>
174+
% else:
175+
<span class="label label-warning">${ _('OFFLINE') }</span>
176+
% endif
177+
</td>
178+
</tr>
193179
% endfor
194-
</tbody>
195-
</table>
180+
</tbody>
181+
</table>
182+
</br>
183+
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
184+
</br>
185+
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
186+
<thead>
187+
<tr>
188+
<th>${ _('Name') }</th>
189+
<th>${ _('Status') }</th>
190+
</tr>
191+
</thead>
192+
<tbody>
193+
% for consumer in cluster['consumer_groups']:
194+
<tr>
195+
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['cluster']['id'], group_id=consumer)}">${consumer}</a></td>
196+
<td>
197+
% if cluster['consumer_groups_status'][consumer] == 0:
198+
<span class="label label-warning">${ _('OFFLINE') }</span>
199+
% else:
200+
<span class="label label-success">${ _('ONLINE') }</span>
201+
% endif
202+
</td>
203+
</tr>
204+
% endfor
205+
</tbody>
206+
</table>
207+
% else:
208+
<h4 class="card-heading simple">${ _('Brokers') }</h4>
209+
</br>
210+
<div class="alert alert-error">${ _('No data available') }</div>
211+
<br>
212+
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
213+
</br>
214+
<div class="alert alert-error">${ _('No data available') }</div>
215+
<br>
216+
217+
% endif
218+
196219
</div>
197220
</div>
198221
</div>

‎kafka/src/kafka/templates/consumer_group.mako‎

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
%>
2121
<%namespace name="kafka" file="navigation_bar.mako" />
2222

23-
${commonheader("%s > Consumer Group > %s" % (cluster['nice_name'], consumer_group['id'] ), app_name, user) | n,unicode}
23+
% if error == 0:
24+
${commonheader("%s > Consumer Group > %s" % (cluster['nice_name'], consumer_group['id'] ), app_name, user) | n,unicode}
25+
% else:
26+
${commonheader("%s > Consumer Group " % (cluster['nice_name']), app_name, user) | n,unicode}
27+
% endif
2428

2529
## DATATABLE SECTION FOR CONSUMERS
2630

@@ -35,6 +39,8 @@ ${commonheader("%s > Consumer Group > %s" % (cluster['nice_name'], consumer_grou
3539
"bLengthChange":true,
3640
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
3741
"oLanguage":{
42+
"sLengthMenu":"${_('Show _MENU_ entries')}",
43+
"sSearch":"${_('Search')}",
3844
"sEmptyTable":"${_('No data available')}",
3945
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
4046
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -55,6 +61,8 @@ ${commonheader("%s > Consumer Group > %s" % (cluster['nice_name'], consumer_grou
5561
"bLengthChange":true,
5662
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
5763
"oLanguage":{
64+
"sLengthMenu":"${_('Show _MENU_ entries')}",
65+
"sSearch":"${_('Search')}",
5866
"sEmptyTable":"${_('No data available')}",
5967
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
6068
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -72,14 +80,26 @@ ${commonheader("%s > Consumer Group > %s" % (cluster['nice_name'], consumer_grou
7280
7381
</script>
7482

75-
<%
76-
_breadcrumbs = [
77-
["Clusters", url('kafka:index')],
78-
[cluster['nice_name'].lower(), url('kafka:cluster', cluster_id=cluster['id'])],
79-
["Consumer Groups", url('kafka:consumer_groups', cluster_id=cluster['id'])],
80-
[consumer_group['id'], url('kafka:consumer_group', cluster_id=cluster['id'], group_id=consumer_group['id'])],
81-
]
82-
%>
83+
84+
% if error == 0:
85+
<%
86+
_breadcrumbs = [
87+
["Clusters", url('kafka:index')],
88+
[cluster['nice_name'].lower(), url('kafka:cluster', cluster_id=cluster['id'])],
89+
["Consumer Groups", url('kafka:consumer_groups', cluster_id=cluster['id'])],
90+
[consumer_group['id'], url('kafka:consumer_group', cluster_id=cluster['id'], group_id=consumer_group['id'])],
91+
]
92+
%>
93+
% else:
94+
<%
95+
_breadcrumbs = [
96+
["Clusters", url('kafka:index')],
97+
[cluster['nice_name'].lower(), url('kafka:cluster', cluster_id=cluster['id'])],
98+
["Consumer Groups", url('kafka:consumer_groups', cluster_id=cluster['id'])],
99+
100+
]
101+
%>
102+
% endif
83103

84104
% if not cluster:
85105
<div class="container-fluid">
@@ -102,72 +122,84 @@ ${ kafka.menubar(section='Consumer Groups',c_id=cluster['id']) }
102122

103123
<div class="container-fluid">
104124
<div class="card">
105-
<h2 class="card-heading simple">${consumer_group['id']}</h2>
125+
% if error == 0:
126+
<h2 class="card-heading simple">${consumer_group['id']}</h2>
127+
% endif
106128
<div class="card-body">
107-
<div class="alert alert-info">${ _('Searching Consumer Groups from path:') } <b>${cluster['consumers_path']}/${consumer_group['id']}</b></div>
108-
<h4 class="card-heading simple">${ _('Consumers') }</h4>
109-
</br>
110-
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupTable" data-tablescroller-disable="true">
111-
<thead>
112-
<tr>
113-
<th>${ _('Name') }</th>
114-
<th>${ _('Topics Subscribed') }</th>
115-
<th>${ _('Status') }</th>
116-
</tr>
117-
</thead>
118-
<tbody>
119-
% if consumer_group['consumers']:
120-
% for consumer in consumer_group['consumers'].keys():
129+
130+
% if error == 1 :
131+
<div class="alert alert-error">
132+
${ _('Error connecting to the zookeper REST server:') } <b>${cluster['zk_rest_url']}</b><br>
133+
${ _('Please contact your administrator to solve this.') }
134+
</div>
135+
% else:
136+
137+
<div class="alert alert-info">${ _('Searching Consumer Groups from path:') } <b>${cluster['consumers_path']}/${consumer_group['id']}</b></div>
138+
<h4 class="card-heading simple">${ _('Consumers') }</h4>
139+
</br>
140+
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupTable" data-tablescroller-disable="true">
141+
<thead>
142+
<tr>
143+
<th>${ _('Name') }</th>
144+
<th>${ _('Topics Subscribed') }</th>
145+
<th>${ _('Status') }</th>
146+
</tr>
147+
</thead>
148+
<tbody>
149+
% if consumer_group['consumers']:
150+
% for consumer in consumer_group['consumers'].keys():
151+
<tr>
152+
<td>${consumer}</td>
153+
<td>
154+
% for topic_subscribed in consumer_group['consumers'][consumer]:
155+
${topic_subscribed}<br>
156+
% endfor
157+
</td>
158+
<td><span class="label label-success">${ _('OK') }</span></td>
159+
</tr>
160+
% endfor
161+
% endif
162+
</tbody>
163+
</table>
164+
</br>
165+
<h4 class="card-heading simple">${ _('Topics Subscribed') }</h4>
166+
</br>
167+
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupTopicsTable" data-tablescroller-disable="true">
168+
<thead>
169+
<tr>
170+
<th>${ _('Topic') }</th>
171+
<th>${ _('Partition - Offset') }</th>
172+
<th>${ _('Partition - Owner') }</th>
173+
<th>${ _('Status') }</th>
174+
</tr>
175+
</thead>
176+
<tbody>
177+
% for topic_offset in consumer_group['offsets']:
121178
<tr>
122-
<td>${consumer}</td>
179+
<td>${topic_offset['topic']}</td>
123180
<td>
124-
% for topic_subscribed in consumer_group['consumers'][consumer]:
125-
${topic_subscribed}<br>
181+
% for partition in topic_offset['offsets'].keys():
182+
${partition} - ${topic_offset['offsets'][partition]}<br>
183+
% endfor
184+
</td>
185+
<td>
186+
% for topic_owner in consumer_group['owners']:
187+
% if topic_offset['topic'] == topic_owner['topic']:
188+
% for partition in topic_owner['owners']:
189+
${partition} - ${topic_owner['owners'][partition]}<br>
190+
% endfor
191+
% else:
192+
<% continue %>
193+
% endif
126194
% endfor
127195
</td>
128196
<td><span class="label label-success">${ _('OK') }</span></td>
129197
</tr>
130198
% endfor
131-
% endif
132-
</tbody>
199+
</tbody>
133200
</table>
134-
</br>
135-
<h4 class="card-heading simple">${ _('Topics Subscribed') }</h4>
136-
</br>
137-
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupTopicsTable" data-tablescroller-disable="true">
138-
<thead>
139-
<tr>
140-
<th>${ _('Topic') }</th>
141-
<th>${ _('Partition - Offset') }</th>
142-
<th>${ _('Partition - Owner') }</th>
143-
<th>${ _('Status') }</th>
144-
</tr>
145-
</thead>
146-
<tbody>
147-
% for topic_offset in consumer_group['offsets']:
148-
<tr>
149-
<td>${topic_offset['topic']}</td>
150-
<td>
151-
% for partition in topic_offset['offsets'].keys():
152-
${partition} - ${topic_offset['offsets'][partition]}<br>
153-
% endfor
154-
</td>
155-
<td>
156-
% for topic_owner in consumer_group['owners']:
157-
% if topic_offset['topic'] == topic_owner['topic']:
158-
% for partition in topic_owner['owners']:
159-
${partition} - ${topic_owner['owners'][partition]}<br>
160-
% endfor
161-
% else:
162-
<% continue %>
163-
% endif
164-
% endfor
165-
</td>
166-
<td><span class="label label-success">${ _('OK') }</span></td>
167-
</tr>
168-
% endfor
169-
</tbody>
170-
</table>
201+
202+
% endif
171203
</div>
172204
</div>
173205
</div>

‎kafka/src/kafka/templates/consumer_groups.mako‎

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ ${commonheader("%s > Consumer Groups" % (cluster['nice_name']), app_name, user)
3535
"bLengthChange":true,
3636
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
3737
"oLanguage":{
38+
"sLengthMenu":"${_('Show _MENU_ entries')}",
39+
"sSearch":"${_('Search')}",
3840
"sEmptyTable":"${_('No data available')}",
3941
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
4042
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -83,35 +85,43 @@ ${ kafka.menubar(section='Consumer Groups',c_id=cluster['id']) }
8385
<div class="card">
8486
<h2 class="card-heading simple">${ _('Consumer Groups of Kakfa cluster:') } ${ cluster['id'] }</h2>
8587
<div class="card-body">
86-
<div class="alert alert-info">${ _('Searching Consumer Groups from path:') } <b>${cluster['consumers_path']}</b></div>
87-
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
88-
</br>
89-
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
90-
<thead>
91-
<tr>
92-
<th>${ _('Name') }</th>
93-
<th>${ _('Status') }</th>
94-
<th># ${ _('Consumers active') }</th>
95-
<th># ${ _('Topics Subscribed') }</th>
96-
</tr>
97-
</thead>
98-
<tbody>
99-
% for consumer_group in consumers_groups:
100-
<tr>
101-
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['id'], group_id=consumer_group['id'])}">${consumer_group['id']}</a></td>
102-
<td>
103-
% if len(consumer_group['consumers']) == 0:
104-
<span class="label label-warning">${ _('OFFLINE') }</span>
105-
% else:
106-
<span class="label label-success">${ _('ONLINE') }</span>
107-
% endif
108-
</td>
109-
<td><span class="badge">${len(consumer_group['consumers'])}</span></td>
110-
<td><span class="badge">${len(consumer_group['offsets'])}</span></td>
111-
</tr>
112-
% endfor
113-
</tbody>
114-
</table>
88+
% if error == 1 :
89+
<div class="alert alert-error">
90+
${ _('Error connecting to the zookeper REST server:') } <b>${cluster['zk_rest_url']}</b><br>
91+
${ _('Please contact your administrator to solve this.') }
92+
</div>
93+
% else:
94+
95+
<div class="alert alert-info">${ _('Searching Consumer Groups from path:') } <b>${cluster['consumers_path']}</b></div>
96+
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
97+
</br>
98+
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
99+
<thead>
100+
<tr>
101+
<th>${ _('Name') }</th>
102+
<th>${ _('Status') }</th>
103+
<th># ${ _('Consumers active') }</th>
104+
<th># ${ _('Topics Subscribed') }</th>
105+
</tr>
106+
</thead>
107+
<tbody>
108+
% for consumer_group in consumers_groups:
109+
<tr>
110+
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['id'], group_id=consumer_group['id'])}">${consumer_group['id']}</a></td>
111+
<td>
112+
% if len(consumer_group['consumers']) == 0:
113+
<span class="label label-warning">${ _('OFFLINE') }</span>
114+
% else:
115+
<span class="label label-success">${ _('ONLINE') }</span>
116+
% endif
117+
</td>
118+
<td><span class="badge">${len(consumer_group['consumers'])}</span></td>
119+
<td><span class="badge">${len(consumer_group['offsets'])}</span></td>
120+
</tr>
121+
% endfor
122+
</tbody>
123+
</table>
124+
% endif
115125
</br>
116126
</div>
117127
</div>

‎kafka/src/kafka/templates/dashboard.mako‎

Lines changed: 789 additions & 0 deletions
Large diffs are not rendered by default.

‎kafka/src/kafka/templates/index.mako‎

Lines changed: 122 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ ${commonheader("Topology", app_name, user) | n,unicode}
3737
"sPaginationType":"bootstrap",
3838
"bLengthChange":true,
3939
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
40-
"oLanguage":{
40+
"oLanguage":{
41+
"sLengthMenu":"${_('Show _MENU_ entries')}",
42+
"sSearch":"${_('Search')}",
4143
"sEmptyTable":"${_('No data available')}",
4244
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
4345
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -58,6 +60,8 @@ ${commonheader("Topology", app_name, user) | n,unicode}
5860
"bLengthChange":true,
5961
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
6062
"oLanguage":{
63+
"sLengthMenu":"${_('Show _MENU_ entries')}",
64+
"sSearch":"${_('Search')}",
6165
"sEmptyTable":"${_('No data available')}",
6266
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
6367
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -80,7 +84,7 @@ ${commonheader("Topology", app_name, user) | n,unicode}
8084
]
8185
%>
8286

83-
% if not cluster:
87+
% if not clusters:
8488
<div class="container-fluid">
8589
<div class="card">
8690
<h1 class="card-heading simple">${ _('There are currently no clusters to browse.') }</h1>
@@ -97,101 +101,123 @@ ${commonheader("Topology", app_name, user) | n,unicode}
97101
${ kafka.header(_breadcrumbs) }
98102
% endif
99103

100-
${ kafka.menubar(section='Topology',c_id=cluster['cluster']['id']) }
104+
${ kafka.menubar(section='Topology',c_id=clusters[0]['cluster']['id']) }
105+
106+
% for cluster in clusters:
101107

102-
<div class="container-fluid">
103-
<div class="card">
104-
<h2 class="card-heading simple">${ _('Topology of Kakfa cluster:') } ${ cluster['cluster']['nice_name'] }</h2>
105-
<div class="card-body">
106-
<div class="alert alert-info">${ _('The zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b></div>
108+
<div class="container-fluid">
109+
<div class="card">
110+
<h2 class="card-heading simple">${ _('Topology of Kakfa cluster:') } ${ cluster['cluster']['nice_name'] }</h2>
111+
<div class="card-body">
107112

108-
<h4 class="card-heading simple">${ _('Zookepers') }</h4>
109-
</br>
110-
<table class="table table-hover table-striped table-condensed">
111-
<thead>
112-
<tr>
113-
<th>${ _('Hostname') }</th>
114-
<th>${ _('Port') }</th>
115-
<th>${ _('Status') }</th>
116-
</tr>
117-
</thead>
118-
<tbody>
119-
% for zookeeper in cluster['cluster']['zk_host_ports'].split(','):
120-
<tr>
121-
<td>${zookeeper.split(':')[0]}</td>
122-
<td>${zookeeper.split(':')[1]}</td>
123-
<%
124-
error = test_connection(zookeeper.split(':')[0],int(zookeeper.split(':')[1]))
125-
%>
126-
<td>
127-
% if not error:
128-
<span class="label label-success">${ _('ONLINE') }</span>
129-
% else:
130-
<span class="label label-warning">${ _('OFFLINE') }</span>
131-
% endif
132-
</td>
133-
</tr>
134-
% endfor
135-
</tbody>
136-
</table>
137-
</br>
138-
<h4 class="card-heading simple">${ _('Brokers') }</h4>
139-
</br>
140-
<table class="table datatables table-striped table-hover table-condensed" id="brokersTable" data-tablescroller-disable="true">
141-
<thead>
142-
<tr>
143-
<th>${ _('Broker ID') }</th>
144-
<th>${ _('Hostname') }</th>
145-
<th>${ _('Port') }</th>
146-
<th>${ _('Status') }</th>
147-
</tr>
148-
</thead>
149-
<tbody>
150-
% for broker in cluster['brokers']:
151-
<tr>
152-
<td>${broker['id']}</td>
153-
<td>${broker['host']}</td>
154-
<td>${broker['port']}</td>
155-
<%
156-
error = test_connection(broker['host'],broker['port'])
157-
%>
158-
<td>
159-
% if not error:
160-
<span class="label label-success">${ _('ONLINE') }</span>
161-
% else:
162-
<span class="label label-warning">${ _('OFFLINE') }</span>
163-
% endif
164-
</td>
165-
</tr>
166-
% endfor
167-
</tbody>
168-
</table>
169-
</br>
170-
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
171-
</br>
172-
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
173-
<thead>
174-
<tr>
175-
<th>${ _('Name') }</th>
176-
<th>${ _('Status') }</th>
177-
</tr>
178-
</thead>
179-
<tbody>
180-
% for consumer in cluster['consumer_groups']:
181-
<tr>
182-
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['cluster']['id'], group_id=consumer)}">${consumer}</a></td>
183-
<td>
184-
% if cluster['consumer_groups_status'][consumer] == 0:
185-
<span class="label label-warning">${ _('OFFLINE') }</span>
186-
% else:
187-
<span class="label label-success">${ _('ONLINE') }</span>
188-
% endif
189-
</td>
190-
</tr>
191-
% endfor
192-
</tbody>
193-
</table>
113+
% if cluster['error'] == 0 :
114+
<div class="alert alert-info">${ _('The zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b></div>
115+
% else:
116+
<div class="alert alert-error">
117+
${ _('Error connecting to the zookeper REST server:') } <b>${cluster['cluster']['zk_rest_url']}</b><br>
118+
${ _('Please contact your administrator to solve this.') }
119+
</div>
120+
% endif
121+
122+
<h4 class="card-heading simple">${ _('Zookepers') }</h4>
123+
</br>
124+
<table class="table table-hover table-striped table-condensed">
125+
<thead>
126+
<tr>
127+
<th>${ _('Hostname') }</th>
128+
<th>${ _('Port') }</th>
129+
<th>${ _('Status') }</th>
130+
</tr>
131+
</thead>
132+
<tbody>
133+
% for zookeeper in cluster['cluster']['zk_host_ports'].split(','):
134+
<tr>
135+
<td>${zookeeper.split(':')[0]}</td>
136+
<td>${zookeeper.split(':')[1]}</td>
137+
<%
138+
error = test_connection(zookeeper.split(':')[0],int(zookeeper.split(':')[1]))
139+
%>
140+
<td>
141+
% if not error:
142+
<span class="label label-success">${ _('ONLINE') }</span>
143+
% else:
144+
<span class="label label-warning">${ _('OFFLINE') }</span>
145+
% endif
146+
</td>
147+
</tr>
148+
% endfor
149+
</tbody>
150+
</table>
151+
</br>
152+
% if cluster['error'] == 0 :
153+
<h4 class="card-heading simple">${ _('Brokers') }</h4>
154+
</br>
155+
<table class="table datatables table-striped table-hover table-condensed" id="brokersTable" data-tablescroller-disable="true">
156+
<thead>
157+
<tr>
158+
<th>${ _('Broker ID') }</th>
159+
<th>${ _('Hostname') }</th>
160+
<th>${ _('Port') }</th>
161+
<th>${ _('Status') }</th>
162+
</tr>
163+
</thead>
164+
<tbody>
165+
% for broker in cluster['brokers']:
166+
<tr>
167+
<td>${broker['id']}</td>
168+
<td>${broker['host']}</td>
169+
<td>${broker['port']}</td>
170+
<%
171+
error = test_connection(broker['host'],broker['port'])
172+
%>
173+
<td>
174+
% if not error:
175+
<span class="label label-success">${ _('ONLINE') }</span>
176+
% else:
177+
<span class="label label-warning">${ _('OFFLINE') }</span>
178+
% endif
179+
</td>
180+
</tr>
181+
% endfor
182+
</tbody>
183+
</table>
184+
</br>
185+
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
186+
</br>
187+
<table class="table datatables table-striped table-hover table-condensed" id="consumerGroupsTable" data-tablescroller-disable="true">
188+
<thead>
189+
<tr>
190+
<th>${ _('Name') }</th>
191+
<th>${ _('Status') }</th>
192+
</tr>
193+
</thead>
194+
<tbody>
195+
% for consumer in cluster['consumer_groups']:
196+
<tr>
197+
<td><a href="${url('kafka:consumer_group', cluster_id=cluster['cluster']['id'], group_id=consumer)}">${consumer}</a></td>
198+
<td>
199+
% if cluster['consumer_groups_status'][consumer] == 0:
200+
<span class="label label-warning">${ _('OFFLINE') }</span>
201+
% else:
202+
<span class="label label-success">${ _('ONLINE') }</span>
203+
% endif
204+
</td>
205+
</tr>
206+
% endfor
207+
</tbody>
208+
</table>
209+
% else:
210+
<h4 class="card-heading simple">${ _('Brokers') }</h4>
211+
</br>
212+
<div class="alert alert-error">${ _('No data available') }</div>
213+
<br>
214+
<h4 class="card-heading simple">${ _('Consumer Groups') }</h4>
215+
</br>
216+
<div class="alert alert-error">${ _('No data available') }</div>
217+
<br>
218+
% endif
219+
</div>
220+
</div>
194221
</div>
195-
</div>
196-
</div>
222+
% endfor
197223
${commonfooter(messages) | n,unicode}

‎kafka/src/kafka/templates/navigation_bar.mako‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def is_selected(section, matcher):
3838
<span class="caret"></span>
3939
</a>
4040
<ul class="dropdown-menu">
41-
% for cluster in CLUSTERS.get().keys():
41+
% for cluster in CLUSTERS.get().keys():
4242
<li>
4343
<a href="${ url('kafka:cluster', cluster_id=cluster) }">
4444
${cluster}
@@ -77,10 +77,10 @@ def is_selected(section, matcher):
7777
Kafka
7878
</a>
7979
</li>
80-
<li class="${is_selected(section, 'Topology')}"><a href="${ url('kafka:cluster', cluster_id=c_id) }">
81-
${ _('Topology') }</a></li>
80+
<li class="${is_selected(section, 'Topology')}"><a href="${ url('kafka:cluster', cluster_id=c_id) }">${ _('Topology') }</a></li>
8281
<li class="${is_selected(section, 'Consumer Groups')}"><a href="${url('kafka:consumer_groups', cluster_id=c_id)}">${ _('Consumer Groups') }</a></li>
8382
<li class="${is_selected(section, 'Topics')}"><a href="${url('kafka:topics', cluster_id=c_id)}">${ _('Topics') }</a></li>
83+
<li class="${is_selected(section, 'Dashboard')}"><a href="${url('kafka:dashboard', cluster_id=c_id)}">${ _('Dashboard') }</a></li>
8484
</ul>
8585
</div>
8686
</div>

‎kafka/src/kafka/templates/topics.mako‎

Lines changed: 56 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ ${commonheader("%s > Topics" % (cluster['nice_name']), app_name, user) | n,unico
3535
"bLengthChange":true,
3636
"sDom": "<'row-fluid'<l><f>r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
3737
"oLanguage":{
38+
"sLengthMenu":"${_('Show _MENU_ entries')}",
39+
"sSearch":"${_('Search')}",
3840
"sEmptyTable":"${_('No data available')}",
3941
"sInfo":"${_('Showing _START_ to _END_ of _TOTAL_ entries')}",
4042
"sInfoEmpty":"${_('Showing 0 to 0 of 0 entries')}",
@@ -56,7 +58,7 @@ ${commonheader("%s > Topics" % (cluster['nice_name']), app_name, user) | n,unico
5658
_breadcrumbs = [
5759
["Clusters", url('kafka:index')],
5860
[cluster['nice_name'].lower(), url('kafka:cluster', cluster_id=cluster['id'])],
59-
["Topics", url('kafka:topics', cluster_id=cluster['id'])],
61+
[_('Topics'), url('kafka:topics', cluster_id=cluster['id'])],
6062
]
6163
%>
6264

@@ -81,51 +83,60 @@ ${ kafka.menubar(section='Topics',c_id=cluster['id']) }
8183

8284
<div class="container-fluid">
8385
<div class="card">
84-
<h2 class="card-heading simple">${ _('Topics of Kakfa cluster:') } ${ cluster['id'] }</h2>
86+
<h2 class="card-heading simple">${ _('Topics of Kakfa cluster:') } ${ cluster['nice_name'] }</h2>
8587
<div class="card-body">
86-
<div class="alert alert-info">${ _('Searching topics from path:') } <b>${cluster['topics_path']}</b></div>
87-
<h4 class="card-heading simple">${ _('Topics') }</h4>
88-
</br>
89-
<table class="table datatables table-striped table-hover table-condensed" id="topicsTable" data-tablescroller-disable="true">
90-
<thead>
91-
<tr>
92-
<th>${ _('Name') }</th>
93-
<th># ${ _('Partitions') }</th>
94-
<th>${ _('Partitions ids') }</th>
95-
<th># ${ _('Replicas / Partition') }</th>
96-
<th>${ _('Partition - Replicas ids in isr') }</th>
97-
<th>${ _('Partition - Leader') }</th>
98-
<th>${ _('Status') }</th>
99-
</tr>
100-
</thead>
101-
<tbody>
102-
% for topic in topics:
103-
<tr>
104-
<td>${topic['id']}</td>
105-
<td><span class="badge">${len(topic['partitions'])}</span></td>
106-
<td>[
107-
% for partition in topic['partitions']:
108-
${partition}
109-
% endfor
110-
]
111-
</td>
112-
<td><span class="badge">${len(topic['topic_partitions_data'][topic['partitions'][0]])}</span</td>
113-
<td>
114-
% for partition in topic['partitions']:
115-
${partition} - ${topic['topic_partitions_states'][partition]['isr']}<br>
116-
% endfor
117-
</td>
118-
<td>
119-
% for partition in topic['partitions']:
120-
${partition} - ${topic['topic_partitions_states'][partition]['leader']}<br>
121-
% endfor
122-
</td>
123-
<td><span class="label label-success">${ _('OK') }</span></td>
124-
</tr>
125-
% endfor
126-
</tbody>
127-
</table>
128-
</br>
88+
89+
% if error == 1 :
90+
<div class="alert alert-error">
91+
${ _('Error connecting to the zookeper REST server:') } <b>${cluster['zk_rest_url']}</b><br>
92+
${ _('Please contact your administrator to solve this.') }
93+
</div>
94+
95+
% else:
96+
<div class="alert alert-info">${ _('Searching topics from path:') } <b>${cluster['topics_path']}</b></div>
97+
<h4 class="card-heading simple">${ _('Topics') }</h4>
98+
</br>
99+
<table class="table datatables table-striped table-hover table-condensed" id="topicsTable" data-tablescroller-disable="true">
100+
<thead>
101+
<tr>
102+
<th>${ _('Name') }</th>
103+
<th># ${ _('Partitions') }</th>
104+
<th>${ _('Partitions ids') }</th>
105+
<th># ${ _('Replicas / Partition') }</th>
106+
<th>${ _('Partition - Replicas ids in isr') }</th>
107+
<th>${ _('Partition - Leader') }</th>
108+
<th>${ _('Status') }</th>
109+
</tr>
110+
</thead>
111+
<tbody>
112+
% for topic in topics:
113+
<tr>
114+
<td>${topic['id']}</td>
115+
<td><span class="badge">${len(topic['partitions'])}</span></td>
116+
<td>[
117+
% for partition in topic['partitions']:
118+
${partition}
119+
% endfor
120+
]
121+
</td>
122+
<td><span class="badge">${len(topic['topic_partitions_data'][topic['partitions'][0]])}</span</td>
123+
<td>
124+
% for partition in topic['partitions']:
125+
${partition} - ${topic['topic_partitions_states'][partition]['isr']}<br>
126+
% endfor
127+
</td>
128+
<td>
129+
% for partition in topic['partitions']:
130+
${partition} - ${topic['topic_partitions_states'][partition]['leader']}<br>
131+
% endfor
132+
</td>
133+
<td><span class="label label-success">${ _('OK') }</span></td>
134+
</tr>
135+
% endfor
136+
</tbody>
137+
</table>
138+
</br>
139+
% endif
129140
</div>
130141
</div>
131142
</div>

‎kafka/src/kafka/urls.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
# See the License for the specific language governing permissions and
1616
# limitations under the License.
1717

18-
from django.conf.urls.defaults import patterns, url
18+
try:
19+
from django.conf.urls.defaults import patterns, url
20+
except:
21+
from django.conf.urls import patterns, url
1922

2023
IS_URL_NAMESPACED = True
2124

@@ -25,4 +28,6 @@
2528
url(r'^(?P<cluster_id>\w+)/topics/$', 'topics', name="topics"),
2629
url(r'^(?P<cluster_id>\w+)/consumer_groups/$', 'consumer_groups', name="consumer_groups"),
2730
url(r'^(?P<cluster_id>\w+)/consumer_group/(?P<group_id>.+)$', 'consumer_group', name="consumer_group"),
31+
url(r'^(?P<cluster_id>\w+)/dashboard/$', 'dashboard', name="dashboard"),
32+
url(r'^(?P<cluster_id>\w+)/getjson/(?P<type>.+)/$', '_get_json_type', name="_get_json_type"),
2833
)

‎kafka/src/kafka/utils.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323

2424
def get_cluster_or_404(id):
25+
""" Get a cluster information from its ID or a 404 Error """
2526
try:
2627
name = id
2728
cluster = CLUSTERS.get()[name]
@@ -36,11 +37,14 @@ def get_cluster_or_404(id):
3637
'brokers_path' : cluster.BROKERS_PATH.get(),
3738
'consumers_path' : cluster.CONSUMERS_PATH.get(),
3839
'topics_path' : cluster.TOPICS_PATH.get(),
40+
'ganglia_server' : cluster.GANGLIA_SERVER.get(),
41+
'ganglia_data_source' : cluster.GANGLIA_DATA_SOURCE.get(),
3942
}
4043

4144
return cluster
4245

4346
def test_connection (host, port):
47+
""" Test available connection of a given host and port """
4448
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4549
try:
4650
s.connect((host, port))

‎kafka/src/kafka/views.py‎

Lines changed: 258 additions & 135 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
This repository has been archived.