Explorar o código

[zookeeper] Porting to current master

Support only one cluster for now.
Need restlying.
Need to change url zookeeper ids by just cluster name.
Need to i18m.
Romain Rigaux %!s(int64=12) %!d(string=hai) anos
pai
achega
23eff10
Modificáronse 38 ficheiros con 651 adicións e 652 borrados
  1. 4 2
      apps/Makefile
  2. 1 0
      apps/zookeeper/Makefile
  3. 1 0
      apps/zookeeper/hueversion.py
  4. 8 24
      apps/zookeeper/setup.py
  5. 0 32
      apps/zookeeper/src/zkui/static/bootstrap.js
  6. 0 62
      apps/zookeeper/src/zkui/static/css/zkui.css
  7. 0 50
      apps/zookeeper/src/zkui/static/js/Source/Zkui/Zkui.js
  8. 0 5
      apps/zookeeper/src/zkui/static/js/package.yml
  9. 0 33
      apps/zookeeper/src/zkui/templates/clients.mako
  10. 0 17
      apps/zookeeper/src/zkui/templates/create.mako
  11. 0 17
      apps/zookeeper/src/zkui/templates/edit.mako
  12. 0 37
      apps/zookeeper/src/zkui/templates/index.mako
  13. 0 53
      apps/zookeeper/src/zkui/templates/shared_components.mako
  14. 0 58
      apps/zookeeper/src/zkui/templates/tree.mako
  15. 5 5
      apps/zookeeper/src/zookeeper/__init__.py
  16. 39 0
      apps/zookeeper/src/zookeeper/conf.py
  17. 8 6
      apps/zookeeper/src/zookeeper/forms.py
  18. 5 6
      apps/zookeeper/src/zookeeper/models.py
  19. 10 12
      apps/zookeeper/src/zookeeper/rest.py
  20. 9 17
      apps/zookeeper/src/zookeeper/settings.py
  21. 0 0
      apps/zookeeper/src/zookeeper/static/art/icon_24.png
  22. 0 0
      apps/zookeeper/src/zookeeper/static/art/line_icons.png
  23. 59 0
      apps/zookeeper/src/zookeeper/static/css/zookeeper.css
  24. 0 0
      apps/zookeeper/src/zookeeper/static/help/index.html
  25. 11 9
      apps/zookeeper/src/zookeeper/stats.py
  26. 56 0
      apps/zookeeper/src/zookeeper/templates/clients.mako
  27. 42 0
      apps/zookeeper/src/zookeeper/templates/create.mako
  28. 42 0
      apps/zookeeper/src/zookeeper/templates/edit.mako
  29. 62 0
      apps/zookeeper/src/zookeeper/templates/index.mako
  30. 43 0
      apps/zookeeper/src/zookeeper/templates/shared_components.mako
  31. 83 0
      apps/zookeeper/src/zookeeper/templates/tree.mako
  32. 34 9
      apps/zookeeper/src/zookeeper/templates/view.mako
  33. 15 13
      apps/zookeeper/src/zookeeper/urls.py
  34. 19 10
      apps/zookeeper/src/zookeeper/utils.py
  35. 61 56
      apps/zookeeper/src/zookeeper/views.py
  36. 10 8
      apps/zookeeper/src/zookeeper/windmilltests.py
  37. 12 52
      desktop/conf.dist/hue.ini
  38. 12 59
      desktop/conf/pseudo-distributed.ini.tmpl

+ 4 - 2
apps/Makefile

@@ -44,7 +44,8 @@ APPS := about \
   pig \
   search \
   hbase \
-  sqoop
+  sqoop \
+  zookeeper
 
 ################################################
 # Install all applications into the Desktop environment
@@ -117,7 +118,8 @@ I18N_APPS := about \
   pig \
   search \
   hbase \
-  sqoop
+  sqoop \
+  zookeeper
 
 COMPILE_LOCALE_TARGETS := $(I18N_APPS:%=.recursive-compile-locales/%)
 compile-locales: $(COMPILE_LOCALE_TARGETS)

+ 1 - 0
apps/zookeeper/Makefile

@@ -13,6 +13,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+
 ifeq ($(ROOT),)
   $(error "Error: Expect the environment variable $$ROOT to point to the Desktop installation")
 endif

+ 1 - 0
apps/zookeeper/hueversion.py

@@ -0,0 +1 @@
+../../VERSION

+ 8 - 24
apps/zookeeper/setup.py

@@ -13,34 +13,18 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from setuptools import setup, find_packages
-import os
 
-def expand_package_data(src_dirs, strip=""):
-  ret = []
-  for src_dir in src_dirs:
-    for path, dnames, fnames in os.walk(src_dir):
-      for fname in fnames:
-        ret.append(os.path.join(path, fname).replace(strip, ""))
-  return ret
+from setuptools import setup, find_packages
+from hueversion import VERSION
 
-os.chdir(os.path.dirname(os.path.abspath(__file__)))
 setup(
-  name = "zkui",
-  version = "0.1",
-  url = 'http://github.com/andreisavu/hue',
-  description = 'ZooKeeper Browser',
+  name = "zookeeper",
+  version = VERSION,
+  author = "Hue",
+  url = 'http://github.com/cloudera/hue',
+  description = "ZooKeeper Browser",
   packages = find_packages('src'),
   package_dir = {'': 'src'},
   install_requires = ['setuptools', 'desktop'],
-  entry_points = { 'desktop.sdk.application': 'zkui=zkui' },
-  zip_safe = False,
-  package_data = {
-    # Include static resources.  Package_data doesn't
-    # deal well with directory globs, so we enumerate
-    # the files manually.
-    'zkui': expand_package_data(
-      ["src/zkui/templates", "src/zkui/static"],
-      "src/zkui/")
-  }
+  entry_points = { 'desktop.sdk.application': 'zookeeper=zookeeper' },
 )

+ 0 - 32
apps/zookeeper/src/zkui/static/bootstrap.js

@@ -1,32 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-CCS.Desktop.register({
-	Zkui : {
-		name : 'ZooKeeper Browser',
-		css : '/zkui/static/css/zkui.css',
-		require: [ 'Zkui' ],
-		launch: function(path, options){
-			return new Zkui(path || '/zkui/', options);
-		},
-		menu: {
-			id: 'ccs-zkui-menu',
-			img: {
-				src: '/zkui/static/art/zkui.png'
-			}
-		},
-		help: '/help/zkui/'
-	}
-});

+ 0 - 62
apps/zookeeper/src/zkui/static/css/zkui.css

@@ -1,62 +0,0 @@
-/* 
-	zkui styles
-	important: these should be namespaced
-	example:
-		let's say you are creating an app called "calculator"; you should prefix all your styles with your application's name like so
-		
-		.calculator img {
-			border: 1px solid #000;
-		}
-		.calculator p {
-			margin: 10px 0px;
-		}
-		etc...
-
-	other notes:
-	* don't use ids - there may be more than one of your apps open; use classes!
-	* the toolbar element is absolutely positioned and 100% wide (and therefor 0px high);
-	  any elements inside it should also be absolutely positioned
-*/
-
-.zkui img.zkui_icon {
-	width: 55px;
-	height: 55px;
-	position: absolute;
-	top: 27px;
-	left: 3px;
-}
-
-div.zkui .left_col li {
-    margin: 5px 0px;
-    font-size: 16px;
-    background-color: white;
-    color: black;
-    padding: 2px 1px 1px 5px;
-    -moz-border-radius: 3px;
-    -webkit-border-radius: 3px;
-    border: solid black 1px;
-}
-
-div.zkui .left_col li:hover {
-    background-color: lightBlue;
-    color: white;
-}
-
-div.zkui .left_col li a {
-    color: black;
-    display: block;
-}
-
-div.zkui .left_col li a:hover {
-    text-decoration: none;
-}
-
-div.zkui td a.delete {
-//  background: url(/zkui/static/art/line_icons.png) no-repeat 0 -70px;
-}
-
-div.zkui .createZnodeForm td,
-div.zkui .editZnodeForm td {
-  padding: 5px;
-}
-

+ 0 - 50
apps/zookeeper/src/zkui/static/js/Source/Zkui/Zkui.js

@@ -1,50 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/*
----
-
-script: Zkui.js
-
-description: Defines Zkui; a Hue application that extends CCS.JBrowser.
-
-authors:
-- Unknown
-
-requires:
-- ccs-shared/CCS.JBrowser
-
-provides: [Zkui]
-
-...
-*/
-ART.Sheet.define('window.art.browser.zkui', {
-	'min-width': 620
-});
-
-var Zkui = new Class({
-
-	Extends: CCS.JBrowser,
-
-	options: {
-		className: 'art browser logo_header zkui'
-	},
-
-	initialize: function(path, options){
-		this.parent(path || '/zkui/', options);
-	}
-
-});

+ 0 - 5
apps/zookeeper/src/zkui/static/js/package.yml

@@ -1,5 +0,0 @@
-copyright: Apache License v2.0
-version: 0.1
-description: ZooKeeper Browser
-name: ZooKeeper Browser
-sources: [Source/Zkui/Zkui.js]

+ 0 - 33
apps/zookeeper/src/zkui/templates/clients.mako

@@ -1,33 +0,0 @@
-<%namespace name="shared" file="shared_components.mako" />
-
-${shared.header("ZooKeeper Browser > Clients > %s:%s" % (host, port))}
-
-<h1>${host}:${port} :: client connections</h1>
-<br />
-
-% if clients:
-  <table data-filters="HtmlTable"> 
-  <thead>
-    <tr>
-      <th>Host</th>
-      <th>Port</th>
-      <th>Interest Ops</th>
-      <th>Queued</th>
-      <th>Received</th>
-      <th>Sent</th>
-  </thead>
-  % for client in clients:
-    <tr>
-      <td>${client.host}</td>
-      <td>${client.port}</td>
-      <td>${client.interest_ops}</td>
-      <td>${client.queued}</td>
-      <td>${client.recved}</td>
-      <td>${client.sent}</td>
-    </tr>
-  % endfor
-  </table>
-% endif
-
-${shared.footer()}
-

+ 0 - 17
apps/zookeeper/src/zkui/templates/create.mako

@@ -1,17 +0,0 @@
-<%namespace name="shared" file="shared_components.mako" />
-
-${shared.header("ZooKeeper Browser > Create Znode")}
-
-<h2>Create New Znode :: ${path}</h2>
-<hr /><br />
-
-<form class="createZnodeForm" action="" method="POST">
-<table align="center">
-  ${form.as_table()|n}
-<tr><td colspan="2" align="right">
-  <button type="submit">Create</button>
-</td></tr>
-</table>
-</form>
-
-${shared.footer()}

+ 0 - 17
apps/zookeeper/src/zkui/templates/edit.mako

@@ -1,17 +0,0 @@
-<%namespace name="shared" file="shared_components.mako" />
-
-${shared.header("ZooKeeper Browser > Edit Znode > %s" % path)}
-
-<h2>Edit Znode Data :: ${path}</h2>
-<hr /><br />
-
-<form class="editZnodeForm" action="" method="POST">
-<table align="center">
-  ${form.as_table()|n}
-<tr><td colspan="2" align="right">
-  <button type="submit">Save</button>
-</td></tr>
-</table>
-</form>
-
-${shared.footer()}

+ 0 - 37
apps/zookeeper/src/zkui/templates/index.mako

@@ -1,37 +0,0 @@
-<%namespace name="shared" file="shared_components.mako" />
-
-${shared.header("ZooKeeper Browser")}
-
-<h2>Overview</h2>
-
-<br />
-
-% for i, c in enumerate(overview):
-  <h3> ${i+1}. <a href="${url('zkui.views.view', id=i)}">${c['nice_name']} Cluster Overview</a></h3><br />
-
-  <table data-filters="HtmlTable">
-  <thead>
-    <tr>
-      <th>Node</th>
-      <th>Role</th>
-      <th>Avg Latency</th>
-      <th>Watch Count</th>
-      <th>Version</th>
-    </tr>
-  </thead>
-  % for host, stats in c['stats'].items():
-    <tr>
-      <td>${host}</td>
-      <td>${stats.get('zk_server_state', '')}</td>
-      <td>${stats.get('zk_avg_latency', '')}</td>
-      <td>${stats.get('zk_watch_count', '')}</td>
-      <td>${stats.get('zk_version', '')}</td>
-    </tr>
-  % endfor
-  </table>
-
-  <br /><br />
-% endfor 
-
-${shared.footer()}
-

+ 0 - 53
apps/zookeeper/src/zkui/templates/shared_components.mako

@@ -1,53 +0,0 @@
-<%!
-#declare imports here, for example:
-#import datetime
-%>
-
-<%!
-import datetime
-from django.template.defaultfilters import urlencode, escape
-from zkui import settings
-%>
-
-<%def name="header(title='ZooKeeper Browser', toolbar=True)">
-  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
-  <html>
-    <head>
-      <title>${title}</title>
-    </head>
-    <body>
-      % if toolbar:
-      <div class="toolbar">
-        <a href="${url('zkui.views.index')}"><img src="/zkui/static/art/zkui.png" class="zkui_icon"/></a>
-      </div>
-      % endif
-
-    <div data-filters="SplitView">
-    <div class="left_col jframe_padded" style="width:150px;">
-        <ul>
-          <li><a href="${url("zkui.views.index")}">Overview</a></li>
-        </ul>
-        <br />
-
-        <h2>Clusters</h2>
-        <ul>
-            % for id, c in enumerate(settings.CLUSTERS):
-                <li><a href="${url("zkui.views.view", id=id)}">
-                    ${c['nice_name']}</a></li>
-            % endfor
-        </ul>
-    </div>
-
-    <div class="right_col jframe_padded">
-</%def>
-
-<%def name="info_button(url, text)">
-  <a data-filters="ArtButton" href="${url}" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;" data-icon-styles="{'width': 14, 'height': 14}">${text}</a>
-</%def>
-
-<%def name="footer()">
-        </div>
-    </div>
-    </body>
-  </html>
-</%def>

+ 0 - 58
apps/zookeeper/src/zkui/templates/tree.mako

@@ -1,58 +0,0 @@
-<%namespace name="shared" file="shared_components.mako" />
-
-${shared.header("ZooKeeper Browser > Tree > %s > %s" % (cluster['nice_name'], path))}
-
-<h1>${cluster['nice_name'].lower()} :: ${path}</h1>
-<br />
-
-<table data-filters="HtmlTable">
-  <thead>
-  <th colspan="2">Children</th>
-  </thead>
-  % for child in children:
-    <tr><td width="100%">
-      <a href="${url('zkui.views.tree', id=cluster['id'], \
-          path=("%s/%s" % (path, child)).replace('//', '/'))}">
-      ${child}</a>
-    </td><td>
-      <a title="Delete ${child}" class="delete frame_tip confirm_and_post" alt="Are you sure you want to delete ${child}?" href="${url('zkui.views.delete', id=cluster['id'], \
-          path=("%s/%s" % (path, child)).replace('//', '/'))}">Delete</a>
-    </td></tr>
-  % endfor
-</table>
-<br />
-<span style="float: right">
-  ${shared.info_button(url('zkui.views.create', id=cluster['id'], path=path), 'Create New')}
-</span>
-
-<div style="clear: both"></div>
-
-<h2>data :: base64 :: length :: ${znode.get('dataLength', 0)}</h2>
-<br />
-
-<textarea name="data64" style="width: 100%;" rows="5" readonly="readonly">${znode.get('data64', '')}</textarea>
-<div style="clear: both"></div>
-<span style="float: right">
-  ${shared.info_button(url('zkui.views.edit_as_base64', id=cluster['id'], path=path), 'Edit as Base64')}
-  ${shared.info_button(url('zkui.views.edit_as_text', id=cluster['id'], path=path), 'Edit as Text')}
-</span>
-<div style="clear: both"></div>
-<br />
-
-<h2>stat information</h2>
-<br />
-
-<table data-filters="HtmlTable">
-  <thead><tr><th>Key</th>
-    <th width="80%">Value</th></tr></thead>
-  % for key in ('pzxid', 'ctime', 'aversion', 'mzxid', \
-      'ephemeralOwner', 'version', 'mtime', 'cversion', 'czxid'):
-    <tr><td>${key}</td><td>${znode[key]}</td></tr> 
-  % endfor
-</table>
-
-<br />
-<a target="_blank" href="http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#sc_zkStatStructure">Details on stat information.</a>
-
-${shared.footer()}
-

+ 5 - 5
apps/zookeeper/src/zkui/__init__.py → apps/zookeeper/src/zookeeper/__init__.py

@@ -1,16 +1,16 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-

+ 39 - 0
apps/zookeeper/src/zookeeper/conf.py

@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from desktop.lib.conf import Config, UnspecifiedConfigSection, ConfigSection, validate_path, coerce_bool
+
+
+CLUSTERS = UnspecifiedConfigSection(
+  "clusters",
+  help="One entry for each Zookeeper cluster",
+  each=ConfigSection(
+    help="Information about a single Zookeeper cluster",
+    members=dict(
+      HOST_PORTS=Config(
+          "host_ports",
+          help="Zookeeper ensemble. Comma separated list of Host/Port, e.g. localhost:2181,localhost:2182,localhost:2183",
+          default="localhost:2181",
+      ),
+      REST_URL=Config(
+          "rest_url",
+          help="The URL of the REST contrib service.",
+          default="http://localhost:9998",
+      ),
+    )
+  )
+)

+ 8 - 6
apps/zookeeper/src/zkui/forms.py → apps/zookeeper/src/zookeeper/forms.py

@@ -1,29 +1,31 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from django import forms
 from django.forms.widgets import Textarea, HiddenInput
 
+
 class CreateZNodeForm(forms.Form):
   name = forms.CharField(max_length=64)
   data = forms.CharField(required=False, widget=Textarea)
   sequence = forms.BooleanField(required=False)
 
+
 class EditZNodeForm(forms.Form):
   data = forms.CharField(required=False, widget=Textarea)
   version = forms.IntegerField(required=False, widget=HiddenInput)
-  
-

+ 5 - 6
apps/zookeeper/src/zkui/models.py → apps/zookeeper/src/zookeeper/models.py

@@ -1,17 +1,16 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
-

+ 10 - 12
apps/zookeeper/src/zkui/rest.py → apps/zookeeper/src/zookeeper/rest.py

@@ -1,8 +1,8 @@
-
-# Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
@@ -21,6 +21,7 @@ import simplejson
 
 from contextlib import contextmanager
 
+
 class RequestWithMethod(urllib2.Request):
     """ Request class that know how to set the method name """
     def __init__(self, *args, **kwargs):
@@ -34,6 +35,7 @@ class RequestWithMethod(urllib2.Request):
     def set_method(self, method):
         self._method = method
 
+
 class ZooKeeper(object):
 
     class Error(Exception): pass
@@ -46,7 +48,7 @@ class ZooKeeper(object):
 
     class WrongVersion(Error): pass
 
-    def __init__(self, uri = 'http://localhost:9998'):
+    def __init__(self, uri='http://localhost:9998'):
         self._base = uri
         self._session = None
 
@@ -98,7 +100,7 @@ class ZooKeeper(object):
         for child in resp.get('children', []):
             yield child if not uris else resp['child_uri_template']\
               .replace('{child}', urllib2.quote(child))
-       
+
     def create(self, path, data=None, sequence=False, ephemeral=False):
         """ Create a new node. By default this call creates a persistent znode.
 
@@ -117,19 +119,16 @@ class ZooKeeper(object):
             if self._session:
                 flags['session'] = self._session
             else:
-                raise ZooKeeper.Error, 'You need a session '\
-                    'to create an ephemeral node'
+                raise ZooKeeper.Error, 'You need a session to create an ephemeral node'
         flags = urllib.urlencode(flags)
 
-        url = "%s/znodes/v1%s?op=create&name=%s&%s" % \
-            (self._base, head, name, flags)
+        url = "%s/znodes/v1%s?op=create&name=%s&%s" % (self._base, head, name, flags)
 
         return self._do_post(url, data)
 
     def set(self, path, data=None, version=-1, null=False):
         """ Set the value of node """
-        url = "%s/znodes/v1%s?%s" % (self._base, path, \
-            urllib.urlencode({
+        url = "%s/znodes/v1%s?%s" % (self._base, path, urllib.urlencode({
                 'version': version,
                 'null': 'true' if null else 'false'
         }))
@@ -227,4 +226,3 @@ class ZooKeeper(object):
             if e.code == 412: # precondition failed
                 raise ZooKeeper.WrongVersion(uri)
             raise
-

+ 9 - 17
apps/zookeeper/src/zkui/settings.py → apps/zookeeper/src/zookeeper/settings.py

@@ -1,30 +1,22 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-DJANGO_APPS = [ "zkui" ]
+DJANGO_APPS = [ "zookeeper" ]
 NICE_NAME = "ZooKeeper Browser"
-REQUIRES_HADOOP = False
-
-CLUSTERS = [{
-        'nice_name': 'Default',
-        'hostport': 'localhost:2181,localhost:2182,localhost:2183',
-        'rest_gateway': 'http://localhost:9998'
-    }
-]
-
-DEPENDER_PACKAGE_YMLS = [
-    "src/zkui/static/js/package.yml",
-]
+MENU_INDEX = 58
+ICON = '/zookeeper/static/art/icon_24.png'
+IS_URL_NAMESPACED = True

+ 0 - 0
apps/zookeeper/src/zkui/static/art/zkui.png → apps/zookeeper/src/zookeeper/static/art/icon_24.png


+ 0 - 0
apps/zookeeper/src/zkui/static/art/line_icons.png → apps/zookeeper/src/zookeeper/static/art/line_icons.png


+ 59 - 0
apps/zookeeper/src/zookeeper/static/css/zookeeper.css

@@ -0,0 +1,59 @@
+/*
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+.zookeeper img.zookeeper_icon {
+	width: 55px;
+	height: 55px;
+	position: absolute;
+	top: 27px;
+	left: 3px;
+}
+
+div.zookeeper .left_col li {
+    margin: 5px 0px;
+    font-size: 16px;
+    background-color: white;
+    color: black;
+    padding: 2px 1px 1px 5px;
+    -moz-border-radius: 3px;
+    -webkit-border-radius: 3px;
+    border: solid black 1px;
+}
+
+div.zookeeper .left_col li:hover {
+    background-color: lightBlue;
+    color: white;
+}
+
+div.zookeeper .left_col li a {
+    color: black;
+    display: block;
+}
+
+div.zookeeper .left_col li a:hover {
+    text-decoration: none;
+}
+
+div.zookeeper td a.delete {
+//  background: url(/zookeeper/static/art/line_icons.png) no-repeat 0 -70px;
+}
+
+div.zookeeper .createZnodeForm td,
+div.zookeeper .editZnodeForm td {
+  padding: 5px;
+}

+ 0 - 0
apps/zookeeper/src/zkui/static/help/index.html → apps/zookeeper/src/zookeeper/static/help/index.html


+ 11 - 9
apps/zookeeper/src/zkui/stats.py → apps/zookeeper/src/zookeeper/stats.py

@@ -1,13 +1,14 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,6 +20,7 @@ import re
 
 from StringIO import StringIO
 
+
 class Session(object):
 
   class BrokenLine(Exception): pass
@@ -33,7 +35,8 @@ class Session(object):
             k,v = d.split("=")
             self.__dict__[k] = v
     else:
-        raise Session.BrokenLine() 
+        raise Session.BrokenLine()
+
 
 class ZooKeeperStats(object):
 
@@ -93,7 +96,7 @@ class ZooKeeperStats(object):
     def _parse(self, data):
         """ Parse the output from the 'mntr' 4letter word command """
         h = StringIO(data)
-        
+
         result = {}
         for line in h.readlines():
             try:
@@ -109,7 +112,7 @@ class ZooKeeperStats(object):
         h = StringIO(data)
 
         result = {}
-        
+
         version = h.readline()
         if version:
             result['zk_version'] = version[version.index(':')+1:].strip()
@@ -150,7 +153,7 @@ class ZooKeeperStats(object):
                 result['zk_znode_count'] = int(m.group(1))
                 continue
 
-        return result 
+        return result
 
     def _parse_line(self, line):
         try:
@@ -167,4 +170,3 @@ class ZooKeeperStats(object):
             pass
 
         return key, value
-

+ 56 - 0
apps/zookeeper/src/zookeeper/templates/clients.mako

@@ -0,0 +1,56 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${ commonheader(_('Clients'), app_name, user, '100px') | n,unicode }
+${ shared.header("ZooKeeper Browser > Clients > %s:%s" % (host, port)) }
+
+<h1>${host}:${port} :: client connections</h1>
+<br />
+
+% if clients:
+  <table data-filters="HtmlTable">
+  <thead>
+    <tr>
+      <th>Host</th>
+      <th>Port</th>
+      <th>Interest Ops</th>
+      <th>Queued</th>
+      <th>Received</th>
+      <th>Sent</th>
+  </thead>
+  % for client in clients:
+    <tr>
+      <td>${client.host}</td>
+      <td>${client.port}</td>
+      <td>${client.interest_ops}</td>
+      <td>${client.queued}</td>
+      <td>${client.recved}</td>
+      <td>${client.sent}</td>
+    </tr>
+  % endfor
+  </table>
+% endif
+
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 42 - 0
apps/zookeeper/src/zookeeper/templates/create.mako

@@ -0,0 +1,42 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${ commonheader("ZooKeeper Browser > Create Znode", app_name, user, '100px') | n,unicode }
+${ shared.header(clusters) }
+
+<h2>Create New Znode :: ${path}</h2>
+<hr /><br />
+
+<form class="createZnodeForm" action="" method="POST">
+  <table align="center">
+    ${form.as_table()|n}
+    <tr><td colspan="2" align="right">
+    <button type="submit">Create</button>
+    </td></tr>
+  </table>
+</form>
+
+
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 42 - 0
apps/zookeeper/src/zookeeper/templates/edit.mako

@@ -0,0 +1,42 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${ commonheader("ZooKeeper Browser > Edit Znode > %s" % path, app_name, user, '100px') | n,unicode }
+${ shared.header(clusters) }
+
+<h2>Edit Znode Data :: ${path}</h2>
+<hr /><br />
+
+<form class="editZnodeForm" action="" method="POST">
+  <table align="center">
+    ${form.as_table()|n}
+    <tr><td colspan="2" align="right">
+    <button type="submit">Save</button>
+    </td></tr>
+  </table>
+</form>
+
+
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 62 - 0
apps/zookeeper/src/zookeeper/templates/index.mako

@@ -0,0 +1,62 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${ commonheader(_('View'), app_name, user, '100px') | n,unicode }
+${ shared.header(clusters) }
+
+
+<h2>Overview</h2>
+
+<br />
+
+% for i, c in enumerate(clusters):
+  <h3> ${ i + 1 }. <a href="${ url('zookeeper:view', id=i) }">${ c } Cluster Overview</a></h3><br />
+
+  <table data-filters="HtmlTable">
+  <thead>
+    <tr>
+      <th>Node</th>
+      <th>Role</th>
+      <th>Avg Latency</th>
+      <th>Watch Count</th>
+      <th>Version</th>
+    </tr>
+  </thead>
+  % for host, stats in overview[c].items():
+    <tr>
+      <td><a href="${ url('zookeeper:view', id=i) }" data-row-selector="true">${ host }</a></td>
+      <td>${stats.get('zk_server_state', '')}</td>
+      <td>${stats.get('zk_avg_latency', '')}</td>
+      <td>${stats.get('zk_watch_count', '')}</td>
+      <td>${stats.get('zk_version', '')}</td>
+    </tr>
+  % endfor
+  </table>
+
+  <br /><br />
+% endfor
+
+
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 43 - 0
apps/zookeeper/src/zookeeper/templates/shared_components.mako

@@ -0,0 +1,43 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+from django.template.defaultfilters import urlencode, escape
+%>
+
+<%def name="header(clusters)">
+    <div data-filters="SplitView">
+    <div class="left_col jframe_padded" style="width:150px;">
+        <h2>Clusters</h2>
+        <ul>
+            % for id, c in enumerate(clusters):
+                <li>
+                  <a href="${ url('zookeeper:view', id=id) }">
+                    ${ c }
+                  </a>
+                </li>
+            % endfor
+        </ul>
+    </div>
+</%def>
+
+
+<%def name="info_button(url, text)">
+  <a data-filters="ArtButton" href="${url}" style="background: url(/static/art/info.png) left 50%; padding: 6px 6px 6px 20px; margin: 10px;"
+    data-icon-styles="{'width': 14, 'height': 14}">
+      ${text}
+  </a>
+</%def>

+ 83 - 0
apps/zookeeper/src/zookeeper/templates/tree.mako

@@ -0,0 +1,83 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="shared" file="shared_components.mako" />
+
+${ commonheader("ZooKeeper Browser > Tree > %s > %s" % (cluster['nice_name'], path), app_name, user, '100px') | n,unicode }
+${ shared.header(clusters) }
+
+
+<h1>${cluster['nice_name'].lower()} :: ${path}</h1>
+<br />
+
+<table data-filters="HtmlTable">
+  <thead>
+  <th colspan="2">Children</th>
+  </thead>
+  % for child in children:
+    <tr><td width="100%">
+      <a href="${url('zookeeper:tree', id=cluster['id'], \
+          path=("%s/%s" % (path, child)).replace('//', '/'))}">
+      ${child}</a>
+    </td><td>
+      <a title="Delete ${child}" class="delete frame_tip confirm_and_post" alt="Are you sure you want to delete ${child}?" href="${url('zookeeper:delete', id=cluster['id'], \
+          path=("%s/%s" % (path, child)).replace('//', '/'))}">Delete</a>
+    </td></tr>
+  % endfor
+</table>
+<br />
+<span style="float: right">
+  ${shared.info_button(url('zookeeper:create', id=cluster['id'], path=path), 'Create New')}
+</span>
+
+<div style="clear: both"></div>
+
+<h2>data :: base64 :: length :: ${znode.get('dataLength', 0)}</h2>
+<br />
+
+<textarea name="data64" style="width: 100%;" rows="5" readonly="readonly">${znode.get('data64', '')}</textarea>
+<div style="clear: both"></div>
+<span style="float: right">
+  ${shared.info_button(url('zookeeper:edit_as_base64', id=cluster['id'], path=path), 'Edit as Base64')}
+  ${shared.info_button(url('zookeeper:edit_as_text', id=cluster['id'], path=path), 'Edit as Text')}
+</span>
+<div style="clear: both"></div>
+<br />
+
+<h2>stat information</h2>
+<br />
+
+<table data-filters="HtmlTable">
+  <thead><tr><th>Key</th>
+    <th width="80%">Value</th></tr></thead>
+  % for key in ('pzxid', 'ctime', 'aversion', 'mzxid', \
+      'ephemeralOwner', 'version', 'mtime', 'cversion', 'czxid'):
+    <tr><td>${key}</td><td>${znode[key]}</td></tr>
+  % endfor
+</table>
+
+<br />
+<a target="_blank" href="http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#sc_zkStatStructure">Details on stat information.</a>
+
+
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 34 - 9
apps/zookeeper/src/zkui/templates/view.mako → apps/zookeeper/src/zookeeper/templates/view.mako

@@ -1,6 +1,29 @@
+## Licensed to Cloudera, Inc. under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  Cloudera, Inc. licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+<%!
+  from desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
 <%namespace name="shared" file="shared_components.mako" />
 
-${shared.header("ZooKeeper Browser > %s" % cluster['nice_name'])}
+${ commonheader(_('View'), app_name, user, '100px') | n,unicode }
+${ shared.header(clusters) }
+
 
 <%def name="show_stats(stats)">
     <thead>
@@ -40,11 +63,11 @@ ${shared.header("ZooKeeper Browser > %s" % cluster['nice_name'])}
       <td>${stats.get('zk_max_file_descriptor_count', '')}</td>
     </tr>
 
-</%def> 
+</%def>
 
-<h2> ${cluster['nice_name']} Cluster Overview </h2>
+<h2> ${ cluster['nice_name'] } Cluster Overview </h2>
 
-${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Znode Hierarchy')}
+${ shared.info_button(url('zookeeper:tree', id=cluster['id'], path='/'), 'View Znode Hierarchy') }
 
 <br /><br />
 
@@ -72,12 +95,12 @@ ${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Z
 % if leader:
   <h2>node :: ${leader['host']} :: leader</h2>
 
-  ${shared.info_button(url('zkui.views.clients', host=leader['host']), 'View Client Connections')}
+  ${shared.info_button(url('zookeeper:clients', host=leader['host']), 'View Client Connections')}
 
   <br /><br />
   <table data-filters="HtmlTable">
     ${show_stats(leader)}
-    
+
     <tr><td>Followers</td>
       <td>${leader.get('zk_followers', '')}</td>
     </tr>
@@ -89,7 +112,7 @@ ${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Z
     <tr><td>Pending Syncs</td>
       <td>${leader.get('zk_pending_syncs', '')}</td>
     </tr>
-  
+
   </table>
 <br /><br />
 % endif
@@ -98,7 +121,7 @@ ${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Z
   <h2>node :: ${stats['host']} :: follower</h2>
   <br />
 
-  ${shared.info_button(url('zkui.views.clients', host=stats['host']), 'View Client Connections')}
+  ${shared.info_button(url('zookeeper:clients', host=stats['host']), 'View Client Connections')}
 
   <br /><br />
   <table data-filters="HtmlTable">
@@ -107,5 +130,7 @@ ${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Z
   <br /><br />
 % endfor
 
-${shared.footer()}
 
+<link rel="stylesheet" href="/zookeeper/static/css/zookeeper.css">
+
+${ commonfooter(messages) | n,unicode }

+ 15 - 13
apps/zookeeper/src/zkui/urls.py → apps/zookeeper/src/zookeeper/urls.py

@@ -1,13 +1,14 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,13 +17,14 @@
 
 from django.conf.urls.defaults import patterns, url
 
-urlpatterns = patterns('zkui',
-  url(r'^$', 'views.index'),
-  url(r'view/(?P<id>\d+)$', 'views.view'),
-  url(r'clients/(?P<host>.+)$', 'views.clients'),
-  url(r'tree/(?P<id>\d+)(?P<path>.+)$', 'views.tree'),
-  url(r'create/(?P<id>\d+)(?P<path>.*)$', 'views.create'),
-  url(r'delete/(?P<id>\d+)(?P<path>.*)$', 'views.delete'),
-  url(r'edit/base64/(?P<id>\d+)(?P<path>.*)$', 'views.edit_as_base64'),
-  url(r'edit/text/(?P<id>\d+)(?P<path>.*)$', 'views.edit_as_text')
+
+urlpatterns = patterns('zookeeper.views',
+  url(r'^$', 'index', name='index'),
+  url(r'view/(?P<id>\d+)$', 'view', name='view'),
+  url(r'clients/(?P<host>.+)$', 'clients', name='clients'),
+  url(r'tree/(?P<id>\d+)(?P<path>.+)$', 'tree', name='tree'),
+  url(r'create/(?P<id>\d+)(?P<path>.*)$', 'create', name='create'),
+  url(r'delete/(?P<id>\d+)(?P<path>.*)$', 'delete', name='delete'),
+  url(r'edit/base64/(?P<id>\d+)(?P<path>.*)$', 'edit_as_base64', name='edit_as_base64'),
+  url(r'edit/text/(?P<id>\d+)(?P<path>.*)$', 'edit_as_text', name='edit_as_text')
 )

+ 19 - 10
apps/zookeeper/src/zkui/utils.py → apps/zookeeper/src/zookeeper/utils.py

@@ -1,33 +1,42 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from zkui import settings
-
 from django.http import Http404
 
+from zookeeper.conf import CLUSTERS
+
+
 def get_cluster_or_404(id):
   try:
     id = int(id)
-    if not (0 <= id < len(settings.CLUSTERS)):
+    if not (0 <= id < len(CLUSTERS.get())):
       raise ValueError, 'Undefined cluster id.'
   except (TypeError, ValueError):
     raise Http404()
 
-  cluster = settings.CLUSTERS[id]
-  cluster['id'] = id
+  # Support only one cluster
+  name = [k for (k, v) in CLUSTERS.get().iteritems() if v == 0]
+  cluster = CLUSTERS.get()[k]
 
-  return cluster
+  cluster = {
+    'id': id,
+    'nice_name': k,
+    'host_ports': cluster.HOST_PORTS.get(),
+    'rest_url': cluster.REST_URL.get(),
+  }
 
+  return cluster

+ 61 - 56
apps/zookeeper/src/zkui/views.py → apps/zookeeper/src/zookeeper/views.py

@@ -1,13 +1,14 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,58 +18,69 @@
 from desktop.lib.django_util import render
 from django.http import Http404
 
-from zkui import settings
-from zkui.stats import ZooKeeperStats
-from zkui.rest import ZooKeeper
-from zkui.utils import get_cluster_or_404
-from zkui.forms import CreateZNodeForm, EditZNodeForm
+from zookeeper import settings
+
+from zookeeper.conf import CLUSTERS
+from zookeeper.forms import CreateZNodeForm, EditZNodeForm
+from zookeeper.stats import ZooKeeperStats
+from zookeeper.rest import ZooKeeper
+from zookeeper.utils import get_cluster_or_404
+
 
 def _get_global_overview():
-  overview = []
-  for c in settings.CLUSTERS:
-    overview.append(_get_overview(c))
-  return overview
+  clusters = CLUSTERS.get()
+  return dict([(c, _get_overview(clusters[c].HOST_PORTS.get())) for c in clusters])
+
 
-def _get_overview(cluster):
+def _get_overview(host_ports):
   stats = {}
-  for s in cluster['hostport'].split(','):
+
+  for s in host_ports.split(','):
     host, port = map(str.strip, s.split(':'))
 
     zks = ZooKeeperStats(host, port)
     stats[s] = zks.get_stats() or {}
 
-  cluster['stats'] = stats
-  return cluster
+  return stats
 
-def _group_stats_by_role(cluster):
+
+def _group_stats_by_role(stats):
   leader, followers = None, []
-  for host, stats in cluster['stats'].items():
+  for host, stats in stats.items():
     stats['host'] = host
 
     if stats.get('zk_server_state') == 'leader':
       leader = stats
 
     elif stats.get('zk_server_state') == 'follower':
-      followers.append(stats) 
+      followers.append(stats)
+
+  return leader, followers
+
 
-  return leader, followers           
- 
 def index(request):
-  overview = _get_global_overview()  
-  return render('index.mako', request, 
-    dict(overview=overview))
+  overview = _get_global_overview()
+
+  return render('index.mako', request, {
+      'clusters': CLUSTERS.get(),
+      'overview': overview
+  })
+
 
 def view(request, id):
   cluster = get_cluster_or_404(id)
 
-  cluster = _get_overview(cluster)
-  leader, followers = _group_stats_by_role(cluster)
+  stats = _get_overview(cluster['host_ports'])
+  leader, followers = _group_stats_by_role(stats)
+
+  return render('view.mako', request, {
+      'cluster': cluster, 'leader': leader, 'followers': followers,
+      'clusters': CLUSTERS.get(),
+  })
 
-  return render('view.mako', request, 
-    dict(cluster=cluster, leader=leader, followers=followers))
 
 def clients(request, host):
-  parts = host.split(':')  
+  parts = host.split(':')
   if len(parts) != 2:
     raise Http404
 
@@ -79,21 +91,21 @@ def clients(request, host):
   return render('clients.mako', request,
     dict(host=host, port=port, clients=clients))
 
+
 def tree(request, id, path):
   cluster = get_cluster_or_404(id)
-  zk = ZooKeeper(cluster['rest_gateway'])
+  zk = ZooKeeper(cluster['rest_url'])
 
   znode = zk.get(path)
   children = sorted(zk.get_children_paths(path))
-  
-  return render('tree.mako', request,
-    dict(cluster=cluster, path=path, \
-      znode=znode, children=children))
+
+  return render('tree.mako', request, {'cluster': cluster, 'path': path, 'znode': znode, 'children': children, 'clusters': CLUSTERS.get(),})
+
 
 def delete(request, id, path):
   cluster = get_cluster_or_404(id)
   if request.method == 'POST':
-    zk = ZooKeeper(cluster['rest_gateway'])
+    zk = ZooKeeper(cluster['rest_url'])
     try:
       zk.recursive_delete(path)
     except ZooKeeper.NotFound:
@@ -101,30 +113,28 @@ def delete(request, id, path):
 
   return tree(request, id, path[:path.rindex('/')] or '/')
 
+
 def create(request, id, path):
   cluster = get_cluster_or_404(id)
 
   if request.method == 'POST':
     form = CreateZNodeForm(request.POST)
     if form.is_valid():
-      zk = ZooKeeper(cluster['rest_gateway'])
+      zk = ZooKeeper(cluster['rest_url'])
 
-      full_path = ("%s/%s" % (path, form.cleaned_data['name']))\
-        .replace('//', '/')
+      full_path = ("%s/%s" % (path, form.cleaned_data['name'])).replace('//', '/')
 
-      zk.create(full_path, \
-        form.cleaned_data['data'], \
-        sequence = form.cleaned_data['sequence'])
+      zk.create(full_path, form.cleaned_data['data'], sequence = form.cleaned_data['sequence'])
       return tree(request, id, path)
   else:
     form = CreateZNodeForm()
 
-  return render('create.mako', request, 
-    dict(path=path, form=form))
+  return render('create.mako', request, {'path': path, 'form': form, 'clusters': CLUSTERS.get(),})
+
 
 def edit_as_base64(request, id, path):
   cluster = get_cluster_or_404(id)
-  zk = ZooKeeper(cluster['rest_gateway'])
+  zk = ZooKeeper(cluster['rest_url'])
   node = zk.get(path)
 
   if request.method == 'POST':
@@ -137,15 +147,15 @@ def edit_as_base64(request, id, path):
     return tree(request, id, path)
   else:
     form = EditZNodeForm(dict(\
-      data=node.get('data64', ''), 
+      data=node.get('data64', ''),
       version=node.get('version', '-1')))
 
-  return render('edit.mako', request,
-    dict(path=path, form=form))
+  return render('edit.mako', request, {'path': path, 'form': form, 'clusters': CLUSTERS.get(),})
+
 
 def edit_as_text(request, id, path):
   cluster = get_cluster_or_404(id)
-  zk = ZooKeeper(cluster['rest_gateway'])
+  zk = ZooKeeper(cluster['rest_url'])
   node = zk.get(path)
 
   if request.method == 'POST':
@@ -155,11 +165,6 @@ def edit_as_text(request, id, path):
 
     return tree(request, id, path)
   else:
-    form = EditZNodeForm(dict(data=node.get('data64', '')\
-      .decode('base64').strip(), 
-      version=node.get('version', '-1')))
-
-  return render('edit.mako', request,
-    dict(path=path, form=form))
-
+    form = EditZNodeForm(dict(data=node.get('data64', '').decode('base64').strip(), version=node.get('version', '-1')))
 
+  return render('edit.mako', request, {'path': path, 'form': form, 'clusters': CLUSTERS.get(),})

+ 10 - 8
apps/zookeeper/src/zkui/windmilltests.py → apps/zookeeper/src/zookeeper/windmilltests.py

@@ -1,13 +1,14 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-
+#
 #     http://www.apache.org/licenses/LICENSE-2.0
-
+#
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,8 +17,9 @@
 
 from desktop.lib.windmill_util import logged_in_client
 
-def test_zkui():
-  """ launches the default view for zkui """
+
+def test_zookeeper():
+  """ launches the default view for zookeeper """
   client = logged_in_client()
-  client.click(id='ccs-zkui-menu')
-  client.waits.forElement(classname='CCS-ZKUI', timeout='2000')  
+  client.click(id='ccs-zookeeper-menu')
+  client.waits.forElement(classname='CCS-zookeeper', timeout='2000')

+ 12 - 52
desktop/conf.dist/hue.ini

@@ -512,60 +512,20 @@
 
 
 ###########################################################################
-# Settings to configure the Shell application
+# Settings to configure the Zookeeper application.
 ###########################################################################
 
-[shell]
-  # The shell_buffer_amount specifies the number of bytes of output per shell
-  # that the Shell app will keep in memory. If not specified, it defaults to
-  # 524288 (512 MiB).
-  ## shell_buffer_amount=100
-
-  # If you run Hue against a Hadoop cluster with Kerberos security enabled, the
-  # Shell app needs to acquire delegation tokens for the subprocesses to work
-  # correctly. These delegation tokens are stored as temporary files in some
-  # directory. You can configure this directory here. If not specified, it
-  # defaults to /tmp/hue_delegation_tokens.
-  ## shell_delegation_token_dir=/tmp/hue_delegation_tokens
-
-  [[ shelltypes ]]
-
-    # Define and configure a new shell type "pig"
-    # ------------------------------------------------------------------------
-    [[[ pig ]]]
-      nice_name = "Pig Shell (Grunt)"
-      command = "/usr/bin/pig -l /dev/null"
-      help = "The command-line interpreter for Pig"
-
-      [[[[ environment ]]]]
-        # You can specify environment variables for the Pig shell
-        # in this section. Note that JAVA_HOME must be configured
-        # for the Pig shell to run.
-
-        [[[[[ JAVA_HOME ]]]]]
-          value = "/usr/lib/jvm/java-6-sun"
-
-    # Define and configure a new shell type "Sqoop 2"
-    # ------------------------------------------------------------------------
-    [[[ sqoop2 ]]]
-      nice_name = "Sqoop 2 Shell"
-      command = "/usr/bin/sqoop2"
-      help = "The command-line Sqoop 2 client."
-
-      [[[[ environment ]]]]
-        # You can configure environment variables for the Sqoop 2 shell
-        # in this section.
-
-    # Define and configure a new shell type "hbase"
-    # ------------------------------------------------------------------------
-    [[[ hbase ]]]
-      nice_name = "HBase Shell"
-      command = "/usr/bin/hbase shell"
-      help = "The command-line HBase client interface."
-
-      [[[[ environment ]]]]
-        # You can configure environment variables for the HBase shell
-        # in this section.
+[zookeeper]
+
+  [[clusters]]
+
+    [[[default]]]
+      # Zookeeper ensemble. Comma separated list of Host/Port.
+      # e.g. localhost:2181,localhost:2182,localhost:2183
+      ## host_ports=localhost:2181
+
+      # The URL of the REST contrib service
+      ## rest_url=http://localhost:9998
 
 
 ###########################################################################

+ 12 - 59
desktop/conf/pseudo-distributed.ini.tmpl

@@ -516,67 +516,20 @@
 
 
 ###########################################################################
-# Settings to configure the Shell application
+# Settings to configure the Zookeeper application.
 ###########################################################################
 
-[shell]
-  # The shell_buffer_amount specifies the number of bytes of output per shell
-  # that the Shell app will keep in memory. If not specified, it defaults to
-  # 524288 (512 MiB).
-  ## shell_buffer_amount=100
-
-  # If you run Hue against a Hadoop cluster with Kerberos security enabled, the
-  # Shell app needs to acquire delegation tokens for the subprocesses to work
-  # correctly. These delegation tokens are stored as temporary files in some
-  # directory. You can configure this directory here. If not specified, it
-  # defaults to /tmp/hue_delegation_tokens.
-  ## shell_delegation_token_dir=/tmp/hue_delegation_tokens
-
-  [[ shelltypes ]]
-
-    # Define and configure a new shell type "pig"
-    # ------------------------------------------------------------------------
-    [[[ pig ]]]
-      nice_name = "Pig Shell (Grunt)"
-      command = "/usr/bin/pig -l /dev/null"
-      help = "The command-line interpreter for Pig"
-
-      [[[[ environment ]]]]
-        # You can specify environment variables for the Pig shell
-        # in this section. Note that JAVA_HOME must be configured
-        # for the Pig shell to run.
-
-        [[[[[ JAVA_HOME ]]]]]
-          value = "/usr/lib/jvm/java-6-sun"
-
-    # Define and configure a new shell type "hbase"
-    # ------------------------------------------------------------------------
-    [[[ hbase ]]]
-      nice_name = "HBase Shell"
-      command = "/usr/bin/hbase shell"
-      help = "The command-line HBase client interface."
-
-      [[[[ environment ]]]]
-        # You can configure environment variables for the HBase shell
-        # in this section.
-
-    # Define and configure a new shell type "Sqoop 2"
-    # ------------------------------------------------------------------------
-    [[[ sqoop2 ]]]
-      nice_name = "Sqoop 2 Shell"
-      command = "/usr/bin/sqoop2"
-      help = "The command-line Sqoop 2 client."
-
-      [[[[ environment ]]]]
-        # You can configure environment variables for the Sqoop 2 shell
-        # in this section.
-
-    # Define and configure a new shell type "bash" for testing only
-    # ------------------------------------------------------------------------
-    [[[ bash ]]]
-      nice_name = "Bash (Test only!!!)"
-      command = "/bin/bash"
-      help = "A shell that does not depend on Hadoop components"
+[zookeeper]
+
+  [[clusters]]
+
+    [[[default]]]
+      # Zookeeper ensemble. Comma separated list of Host/Port.
+      # e.g. localhost:2181,localhost:2182,localhost:2183
+      ## host_ports=localhost:2181
+
+      # The URL of the REST contrib service
+      ## rest_url=http://localhost:9998
 
 
 ###########################################################################