http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py new file mode 100644 index 0000000..eccc881 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/__init__.py @@ -0,0 +1,16 @@ +# 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. +
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py new file mode 100644 index 0000000..6b1f178 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/forms.py @@ -0,0 +1,29 @@ +# 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. + +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) + + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py new file mode 100644 index 0000000..a46696b --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/models.py @@ -0,0 +1,17 @@ +# 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. + + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py new file mode 100644 index 0000000..e4874a1 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/rest.py @@ -0,0 +1,230 @@ + +# 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. + +import urllib2 +import urllib +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): + urllib2.Request.__init__(self, *args, **kwargs) + self._method = None + + def get_method(self): + return self._method or \ + urllib2.Request.get_method(self) + + def set_method(self, method): + self._method = method + +class ZooKeeper(object): + + class Error(Exception): pass + + class NotFound(Error): pass + + class ZNodeExists(Error): pass + + class InvalidSession(Error): pass + + class WrongVersion(Error): pass + + def __init__(self, uri = 'http://localhost:9998'): + self._base = uri + self._session = None + + def start_session(self, expire=5, id=None): + """ Create a session and return the ID """ + if id is None: + url = "%s/sessions/v1/?op=create&expire=%d" % (self._base, expire) + self._session = self._do_post(url)['id'] + else: + self._session = id + return self._session + + def close_session(self): + """ Close the session on the server """ + if self._session is not None: + url = '%s/sessions/v1/%s' % (self._base, self._session) + self._do_delete(url) + self._session = None + + def heartbeat(self): + """ Send a heartbeat request. This is needed in order to keep a session alive """ + if self._session is not None: + url = '%s/sessions/v1/%s' % (self._base, self._session) + self._do_put(url, '') + + @contextmanager + def session(self, *args, **kwargs): + """ Session handling using a context manager """ + yield self.start_session(*args, **kwargs) + self.close_session() + + def get(self, path): + """ Get a node """ + url = "%s/znodes/v1%s" % (self._base, path) + return self._do_get(url) + + def get_children(self, path): + """ Get all the children for a given path. This function creates a generator """ + for child_path in self.get_children_paths(path, uris=True): + try: + yield self._do_get(child_path) + except ZooKeeper.NotFound: + continue + + def get_children_paths(self, path, uris=False): + """ Get the paths for children nodes """ + url = "%s/znodes/v1%s?view=children" % (self._base, path) + resp = self._do_get(url) + 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. + + You can also create an ephemeral or a sequential znode. + """ + ri = path.rindex('/') + head, name = path[:ri+1], path[ri+1:] + if head != '/': head = head[:-1] + + flags = { + 'null': 'true' if data is None else 'false', + 'ephemeral': 'true' if ephemeral else 'false', + 'sequence': 'true' if sequence else 'false' + } + if ephemeral: + if self._session: + flags['session'] = self._session + else: + 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) + + 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({ + 'version': version, + 'null': 'true' if null else 'false' + })) + return self._do_put(url, data) + + def delete(self, path, version=-1): + """ Delete a znode """ + if type(path) is list: + map(lambda el: self.delete(el, version), path) + return + + url = '%s/znodes/v1%s?%s' % (self._base, path, \ + urllib.urlencode({ + 'version':version + })) + try: + return self._do_delete(url) + except urllib2.HTTPError, e: + if e.code == 412: + raise ZooKeeper.WrongVersion(path) + elif e.code == 404: + raise ZooKeeper.NotFound(path) + raise + + def recursive_delete(self, path): + """ Delete all the nodes from the tree """ + for child in self.get_children_paths(path): + fp = ("%s/%s" % (path, child)).replace('//', '/') + self.recursive_delete(fp) + self.delete(path) + + def exists(self, path): + """ Do a znode exists """ + try: + self.get(path) + return True + except ZooKeeper.NotFound: + return False + + def _do_get(self, uri): + """ Send a GET request and convert errors to exceptions """ + try: + req = urllib2.urlopen(uri) + resp = simplejson.load(req) + + if 'Error' in resp: + raise ZooKeeper.Error(resp['Error']) + + return resp + except urllib2.HTTPError, e: + if e.code == 404: + raise ZooKeeper.NotFound(uri) + raise + + def _do_post(self, uri, data=None): + """ Send a POST request and convert errors to exceptions """ + try: + req = urllib2.Request(uri, {}) + req.add_header('Content-Type', 'application/octet-stream') + if data is not None: + req.add_data(data) + + resp = simplejson.load(urllib2.urlopen(req)) + if 'Error' in resp: + raise ZooKeeper.Error(resp['Error']) + return resp + + except urllib2.HTTPError, e: + if e.code == 201: + return True + elif e.code == 409: + raise ZooKeeper.ZNodeExists(uri) + elif e.code == 401: + raise ZooKeeper.InvalidSession(uri) + raise + + def _do_delete(self, uri): + """ Send a DELETE request """ + req = RequestWithMethod(uri) + req.set_method('DELETE') + req.add_header('Content-Type', 'application/octet-stream') + return urllib2.urlopen(req).read() + + def _do_put(self, uri, data): + """ Send a PUT request """ + try: + req = RequestWithMethod(uri) + req.set_method('PUT') + req.add_header('Content-Type', 'application/octet-stream') + if data is not None: + req.add_data(data) + + return urllib2.urlopen(req).read() + except urllib2.HTTPError, e: + if e.code == 412: # precondition failed + raise ZooKeeper.WrongVersion(uri) + raise + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py new file mode 100644 index 0000000..844c695 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/settings.py @@ -0,0 +1,30 @@ +# 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. + +DJANGO_APPS = [ "zkui" ] +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", +] http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png new file mode 100644 index 0000000..1da4a29 Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/line_icons.png differ http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png new file mode 100644 index 0000000..cb40df3 Binary files /dev/null and b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/art/zkui.png differ http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js new file mode 100644 index 0000000..8e3fbfb --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/bootstrap.js @@ -0,0 +1,32 @@ +// 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/' + } +}); http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css new file mode 100644 index 0000000..c49f392 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/css/zkui.css @@ -0,0 +1,56 @@ +/* + 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. +*/ + +.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 .createZnodeForm td, +div.zkui .editZnodeForm td { + padding: 5px; +} + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html new file mode 100644 index 0000000..355c8cd --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/help/index.html @@ -0,0 +1,26 @@ +<!-- + 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. +--> + +<h1>ZooKeeper Browser</h1> + + +<p>ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services</p> + +<h2>About</h2> + +<p>The ZooKeeper Browser application allows you to see how the cluster nodes are working and also allows you to do CRUD operations on the znode hierarchy.</p> + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js new file mode 100644 index 0000000..c8bf383 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/Source/Zkui/Zkui.js @@ -0,0 +1,50 @@ +// 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); + } + +}); http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml new file mode 100644 index 0000000..c2c07ad --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/static/js/package.yml @@ -0,0 +1,19 @@ +# 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. +copyright: Apache License v2.0 +version: 0.1 +description: ZooKeeper Browser +name: ZooKeeper Browser +sources: [Source/Zkui/Zkui.js] http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py new file mode 100644 index 0000000..48f35dd --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/stats.py @@ -0,0 +1,170 @@ +# 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. + +import socket +import re + +from StringIO import StringIO + +class Session(object): + + class BrokenLine(Exception): pass + + def __init__(self, session): + m = re.search('/(\d+\.\d+\.\d+\.\d+):(\d+)\[(\d+)\]\((.*)\)', session) + if m: + self.host = m.group(1) + self.port = m.group(2) + self.interest_ops = m.group(3) + for d in m.group(4).split(","): + k,v = d.split("=") + self.__dict__[k] = v + else: + raise Session.BrokenLine() + +class ZooKeeperStats(object): + + def __init__(self, host='localhost', port='2181', timeout=1): + self._address = (host, int(port)) + self._timeout = timeout + + def get_stats(self): + """ Get ZooKeeper server stats as a map """ + data = self._send_cmd('mntr') + if data: + return self._parse(data) + else: + data = self._send_cmd('stat') + return self._parse_stat(data) + + def get_clients(self): + """ Get ZooKeeper server clients """ + clients = [] + + stat = self._send_cmd('stat') + if not stat: + return clients + + sio = StringIO(stat) + + #skip two lines + sio.readline() + sio.readline() + + for line in sio: + if not line.strip(): + break + try: + clients.append(Session(line.strip())) + except Session.BrokenLine: + continue + + return clients + + def _create_socket(self): + return socket.socket() + + def _send_cmd(self, cmd): + """ Send a 4letter word command to the server """ + s = self._create_socket() + s.settimeout(self._timeout) + + s.connect(self._address) + s.send(cmd) + + data = s.recv(2048) + s.close() + + return data + + def _parse(self, data): + """ Parse the output from the 'mntr' 4letter word command """ + h = StringIO(data) + + result = {} + for line in h.readlines(): + try: + key, value = self._parse_line(line) + result[key] = value + except ValueError: + pass # ignore broken lines + + return result + + def _parse_stat(self, data): + """ Parse the output from the 'stat' 4letter word command """ + h = StringIO(data) + + result = {} + + version = h.readline() + if version: + result['zk_version'] = version[version.index(':')+1:].strip() + + # skip all lines until we find the empty one + while h.readline().strip(): pass + + for line in h.readlines(): + m = re.match('Latency min/avg/max: (\d+)/(\d+)/(\d+)', line) + if m is not None: + result['zk_min_latency'] = int(m.group(1)) + result['zk_avg_latency'] = int(m.group(2)) + result['zk_max_latency'] = int(m.group(3)) + continue + + m = re.match('Received: (\d+)', line) + if m is not None: + result['zk_packets_received'] = int(m.group(1)) + continue + + m = re.match('Sent: (\d+)', line) + if m is not None: + result['zk_packets_sent'] = int(m.group(1)) + continue + + m = re.match('Outstanding: (\d+)', line) + if m is not None: + result['zk_outstanding_requests'] = int(m.group(1)) + continue + + m = re.match('Mode: (.*)', line) + if m is not None: + result['zk_server_state'] = m.group(1) + continue + + m = re.match('Node count: (\d+)', line) + if m is not None: + result['zk_znode_count'] = int(m.group(1)) + continue + + return result + + def _parse_line(self, line): + try: + key, value = map(str.strip, line.split('\t')) + except ValueError: + raise ValueError('Found invalid line: %s' % line) + + if not key: + raise ValueError('The key is mandatory and should not be empty') + + try: + value = int(value) + except (TypeError, ValueError): + pass + + return key, value + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako new file mode 100644 index 0000000..2bee9a7 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/clients.mako @@ -0,0 +1,51 @@ +<%! +# 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. +%> + +<%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()} + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako new file mode 100644 index 0000000..2a8b8cc --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/create.mako @@ -0,0 +1,34 @@ +<%! +# 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. +%> +<%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()} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako new file mode 100644 index 0000000..997bd07 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/edit.mako @@ -0,0 +1,34 @@ +<%! +# 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. +%> +<%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()} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako new file mode 100644 index 0000000..567919d --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/index.mako @@ -0,0 +1,54 @@ +<%! +# 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. +%> +<%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()} + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako new file mode 100644 index 0000000..f9a4589 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/shared_components.mako @@ -0,0 +1,66 @@ +<%! +# 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. +%> + +<%! +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> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako new file mode 100644 index 0000000..c74c202 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/tree.mako @@ -0,0 +1,75 @@ +<%! +# 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. +%> +<%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://zookeeper.apache.org/docs/current/zookeeperProgrammers.html#sc_zkStatStructure">Details on stat information.</a> + +${shared.footer()} + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako new file mode 100644 index 0000000..e046afc --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/templates/view.mako @@ -0,0 +1,128 @@ +<%! +# 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. +%> +<%namespace name="shared" file="shared_components.mako" /> + +${shared.header("ZooKeeper Browser > %s" % cluster['nice_name'])} + +<%def name="show_stats(stats)"> + <thead> + <tr><th>Key</th> + <th width="100%">Value</th></tr> + </thead> + + <tr><td>Version</td> + <td>${stats.get('zk_version')}</td> + </tr> + + <tr><td>Latency</td><td> + Min: ${stats.get('zk_min_latency', '')} + Avg: ${stats.get('zk_avg_latency', '')} + Max: ${stats.get('zk_max_latency', '')} + </td></tr> + + <tr><td>Packets</td> + <td>Sent: ${stats.get('zk_packets_sent', '')} + Received: ${stats.get('zk_packets_received', '')} + </td> + </tr> + + <tr><td>Outstanding Requests</td> + <td>${stats.get('zk_outstanding_requests', '')}</td> + </tr> + + <tr><td>Watch Count</td> + <td>${stats.get('zk_watch_count', '')}</td> + </tr> + + <tr><td>Open FD Count</td> + <td>${stats.get('zk_open_file_descriptor_count', '')}</td> + </tr> + + <tr><td>Max FD Count</td> + <td>${stats.get('zk_max_file_descriptor_count', '')}</td> + </tr> + +</%def> + +<h2> ${cluster['nice_name']} Cluster Overview </h2> + +${shared.info_button(url('zkui.views.tree', id=cluster['id'], path='/'), 'View Znode Hierarchy')} + +<br /><br /> + +% if leader: +<h2>General</h2> + +<table data-filters="HtmlTable"> + <thead> + <tr><th>Key</th><th width="100%">Value</th></tr> + </thead> + + <tr><td>ZNode Count</td> + <td>${leader.get('zk_znode_count', '')}</td></tr> + + <tr><td>Ephemerals Count</td> + <td>${leader.get('zk_ephemerals_count', '')}</td></tr> + + <tr><td>Approximate Data Size</td> + <td>${leader.get('zk_approximate_data_size', '')} bytes</td></tr> + +</table> +<br /><br /> +% endif + +% if leader: + <h2>node :: ${leader['host']} :: leader</h2> + + ${shared.info_button(url('zkui.views.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> + + <tr><td>Synced Followers</td> + <td>${leader.get('zk_synced_followers', '')}</td> + </tr> + + <tr><td>Pending Syncs</td> + <td>${leader.get('zk_pending_syncs', '')}</td> + </tr> + + </table> +<br /><br /> +% endif + +% for stats in followers: + <h2>node :: ${stats['host']} :: follower</h2> + <br /> + + ${shared.info_button(url('zkui.views.clients', host=stats['host']), 'View Client Connections')} + + <br /><br /> + <table data-filters="HtmlTable"> + ${show_stats(stats)} + </table> + <br /><br /> +% endfor + +${shared.footer()} + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py new file mode 100644 index 0000000..f795f7e --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/urls.py @@ -0,0 +1,28 @@ +# 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. + +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') +) http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py new file mode 100644 index 0000000..fb01317 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/utils.py @@ -0,0 +1,33 @@ +# 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. + +from zkui import settings + +from django.http import Http404 + +def get_cluster_or_404(id): + try: + id = int(id) + if not (0 <= id < len(settings.CLUSTERS)): + raise ValueError, 'Undefined cluster id.' + except (TypeError, ValueError): + raise Http404() + + cluster = settings.CLUSTERS[id] + cluster['id'] = id + + return cluster + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py new file mode 100644 index 0000000..64d926b --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/views.py @@ -0,0 +1,165 @@ +# 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. + +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 + +def _get_global_overview(): + overview = [] + for c in settings.CLUSTERS: + overview.append(_get_overview(c)) + return overview + +def _get_overview(cluster): + stats = {} + for s in cluster['hostport'].split(','): + host, port = map(str.strip, s.split(':')) + + zks = ZooKeeperStats(host, port) + stats[s] = zks.get_stats() or {} + + cluster['stats'] = stats + return cluster + +def _group_stats_by_role(cluster): + leader, followers = None, [] + for host, stats in cluster['stats'].items(): + stats['host'] = host + + if stats.get('zk_server_state') == 'leader': + leader = stats + + elif stats.get('zk_server_state') == 'follower': + followers.append(stats) + + return leader, followers + +def index(request): + overview = _get_global_overview() + return render('index.mako', request, + dict(overview=overview)) + +def view(request, id): + cluster = get_cluster_or_404(id) + + cluster = _get_overview(cluster) + leader, followers = _group_stats_by_role(cluster) + + return render('view.mako', request, + dict(cluster=cluster, leader=leader, followers=followers)) + +def clients(request, host): + parts = host.split(':') + if len(parts) != 2: + raise Http404 + + host, port = parts + zks = ZooKeeperStats(host, port) + clients = zks.get_clients() + + 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']) + + 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)) + +def delete(request, id, path): + cluster = get_cluster_or_404(id) + if request.method == 'POST': + zk = ZooKeeper(cluster['rest_gateway']) + try: + zk.recursive_delete(path) + except ZooKeeper.NotFound: + pass + + 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']) + + full_path = ("%s/%s" % (path, form.cleaned_data['name']))\ + .replace('//', '/') + + 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)) + +def edit_as_base64(request, id, path): + cluster = get_cluster_or_404(id) + zk = ZooKeeper(cluster['rest_gateway']) + node = zk.get(path) + + if request.method == 'POST': + form = EditZNodeForm(request.POST) + if form.is_valid(): + # TODO is valid base64 string? + data = form.cleaned_data['data'].decode('base64') + zk.set(path, data, form.cleaned_data['version']) + + return tree(request, id, path) + else: + form = EditZNodeForm(dict(\ + data=node.get('data64', ''), + version=node.get('version', '-1'))) + + return render('edit.mako', request, + dict(path=path, form=form)) + +def edit_as_text(request, id, path): + cluster = get_cluster_or_404(id) + zk = ZooKeeper(cluster['rest_gateway']) + node = zk.get(path) + + if request.method == 'POST': + form = EditZNodeForm(request.POST) + if form.is_valid(): + zk.set(path, form.cleaned_data['data']) + + 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)) + + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py new file mode 100644 index 0000000..ba44e26 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-huebrowser/zkui/src/zkui/windmilltests.py @@ -0,0 +1,23 @@ +# 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. + +from desktop.lib.windmill_util import logged_in_client + +def test_zkui(): + """ launches the default view for zkui """ + client = logged_in_client() + client.click(id='ccs-zkui-menu') + client.waits.forElement(classname='CCS-ZKUI', timeout='2000') http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt b/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt new file mode 100644 index 0000000..8ccaa1c --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/README.txt @@ -0,0 +1,70 @@ +LogGraph README + +1 - About +LogGraph is an application for viewing and filtering zookeeper logs. It can handle transaction logs and message logs. + +2 - Compiling + +Run "ant jar" in src/contrib/loggraph/. This will download all dependencies and compile all the loggraph code. + +Once compilation has finished, you can run it the the loggraph.sh script in zookeeper-contrib/zookeeper-contrib-loggraph/src/main/resources. +This will start and embedded web server on your machine. +Navigate to http://localhost:8182/graph/main.html + +3 - Usage +LogGraph presents the user with 4 views, + + a) Simple log view + This view simply displays the log text. This isn't very useful without filters (see "Filtering the logs"). + + b) Server view + The server view shows the interactions between the different servers in an ensemble. The X axis represents time. + * Exceptions show up as red dots. Hovering your mouse over them will give you more details of the exception + * The colour of the line represents the election state of the server. + - orange means LOOKING for leader + - dark green means the server is the leader + - light green means the server is following a leader + - yellow means there isn't enough information to determine the state of the server. + * The gray arrows denote election messages between servers. Pink dashed arrows are messages that were sent but never delivered. + + c) Session view + The session view shows the lifetime of sessions on a server. Use the time filter to narrow down the view. Any more than about 2000 events will take a long time to view in your browser. + The X axis represents time. Each line is a session. The black dots represent events on the session. You can click on the black dots for more details of the event. + + d) Stats view + There is currently only one statistics view, Transactions/minute. Suggestions for other statistic views are very welcome. + +4 - Filtering the logs +The logs can be filtered in 2 ways, by time and by content. + +To filter by time simply move the slider to the desired start time. The time window specifies how many milliseconds after and including the start time will be displayed. + +Content filtering uses a adhoc filtering language, using prefix notation. The language looks somewhat similar to lisp. A statement in the language takes the form (op arg arg ....). A statement resolves to a boolean value. Statements can be nested. + +4.1 - Filter arguments +An argument can be a number, a string or a symbol. A number is any argument which starts with -, + or 0 to 9. If the number starts with 0x it is interpretted as hexidecimal. Otherwise it is interpretted as decimal. If the argument begins with a double-quote, (") it is interpretted as a string. Anything else is interpretted as a symbol. + +4.2 - Filter symbols +The possible filter symbols are: + +client-id : number, the session id of the client who initiated a transaction. +cxid : number, the cxid of a transaction +zxid : number, the zxid of a transaction +operation : string, the operation being performed, for example "setData", "createSession", "closeSession", "error", "create" + +4.3 - Filter operations +The possible filter operations are: + +or : logical or, takes 1 or more arguments which must be other statements. +and : logical and, takes 1 or more arguments which must be other statements. +not : logical not, takes 1 argument which must be another statement. +xor : exclusive or, takes 1 or more arguments which must be other statements. += : equals, takes 1 or more arguments, which must all be equal to each other to return true. +> : greater than, takes 1 or more arguments, to return true the 1st argument must be greater than the 2nd argument which must be greater than the 3rd argument and so on... +< : less than, takes 1 or more arguments, to return true the 1st argument must be less than the 2nd argument which must be less than the 3rd argument and so on... + +4.3 - Filter examples +Give me all the setData operations with session id 0xdeadbeef or 0xcafeb33r but not with zxid 0x12341234 -> + +(and (= operation "setData") (or (= client-id 0xdeadbeef) (= client-id 0xcafeb33r)) (not (= zxid 0x12341234))) + http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml new file mode 100644 index 0000000..11143e7 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/build.xml @@ -0,0 +1,70 @@ +<?xml version="1.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. +--> + +<project name="loggraph" default="jar"> + + <import file="../build-contrib.xml"/> + + <target name="init" depends="check-contrib,zookeeperbuildcontrib.init" unless="skip.contrib"> + <echo message="contrib: ${name}"/> + <mkdir dir="${build.dir}"/> + <antcall target="init-contrib"/> + </target> + + <target name="compile" depends="init,ivy-retrieve,zookeeperbuildcontrib.compile" unless="skip.contrib"> + </target> + + <target name="setjarname"> + <property name="jarname" value="${build.dir}/zookeeper-${version}-${name}.jar"/> + </target> + + <target name="jar" depends="setjarname,compile" > + <jar destfile="${jarname}"> + <fileset file="${zk.root}/LICENSE.txt" /> + <fileset dir="${build.classes}" /> + <fileset dir="src/main/webapp"/> + <manifest> + <attribute name="Built-By" value="${user.name}"/> + <attribute name="Built-At" value="${build.time}"/> + <attribute name="Built-On" value="${host.name}" /> + <attribute name="Implementation-Title" value="org.apache.zookeeper.graph"/> + <attribute name="Implementation-Version" value="${revision}"/> + <attribute name="Implementation-Vendor" value="The Apache Software Foundation"/> + </manifest> + </jar> + </target> + + <target name="test"> + <echo message="No test target defined for this package" /> + </target> + + + <target name="package" depends="compile, zookeeperbuildcontrib.package" unless="skip.contrib"> + <echo message="contrib: ${name}"/> + + <copy file="${basedir}/build.xml" todir="${dist.dir}/contrib/${name}"/> + + <mkdir dir="${dist.dir}/contrib/${name}/src"/> + <copy todir="${dist.dir}/contrib/${name}/src"> + <fileset dir="${basedir}/src/main"/> + </copy> + + </target> + +</project> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml b/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml new file mode 100644 index 0000000..d6fa9d6 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/ivy.xml @@ -0,0 +1,44 @@ +<!-- + 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. +--> + +<ivy-module version="2.0" + xmlns:e="http://ant.apache.org/ivy/extra"> + + <info organisation="org.apache.zookeeper" + module="${name}" revision="${version}"> + <license name="Apache 2.0"/> + <ivyauthor name="Apache ZooKeeper" url="http://zookeeper.apache.org"/> + <description>ZooKeeper Graphing</description> + </info> + + <configurations defaultconfmapping="default"> + <conf name="default"/> + <conf name="test"/> + </configurations> + + <dependencies> + <dependency org="org.slf4j" name="slf4j-api" rev="1.6.1"/> + <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.6.1" transitive="false"/> + + <!-- transitive false turns off dependency checking, log4j deps seem borked --> + <dependency org="log4j" name="log4j" rev="1.2.15" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-server" rev="7.0.1.v20091125" /> + <dependency org="org.eclipse.jetty" name="jetty-servlet" rev="7.0.1.v20091125" /> + <dependency org="com.googlecode.json-simple" name="json-simple" rev="1.1" /> + </dependencies> + +</ivy-module> http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java new file mode 100644 index 0000000..c0912fa --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterException.java @@ -0,0 +1,22 @@ +/** + * 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. + */ +package org.apache.zookeeper.graph; + +public class FilterException extends Exception { + public FilterException(String s) { super(s); } +}; http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java new file mode 100644 index 0000000..ee73283 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterOp.java @@ -0,0 +1,75 @@ +/** + * 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. + */ +package org.apache.zookeeper.graph; + +import java.util.ArrayList; +import org.apache.zookeeper.graph.filterops.*; + +public abstract class FilterOp { + protected ArrayList<FilterOp> subOps; + protected ArrayList<Arg> args; + + public enum ArgType { + STRING, NUMBER, SYMBOL + } + + public FilterOp() { + subOps = new ArrayList<FilterOp>(); + args = new ArrayList<Arg>(); + } + + public static FilterOp newOp(String op) throws FilterException { + if (op.equals("or")) + return new OrOp(); + if (op.equals("and")) + return new AndOp(); + if (op.equals("not")) + return new NotOp(); + if (op.equals("xor")) + return new XorOp(); + if (op.equals("=")) + return new EqualsOp(); + if (op.equals("<")) + return new LessThanOp(); + if (op.equals(">")) + return new GreaterThanOp(); + + throw new FilterException("Invalid operation '"+op+"'"); + } + + public void addSubOp(FilterOp op) { + subOps.add(op); + } + + public void addArg(Arg arg) { + args.add(arg); + } + + public abstract boolean matches(LogEntry entry) throws FilterException; + + public String toString() { + String op = "(" + getClass().getName(); + for (FilterOp f : subOps) { + op += " " + f; + } + for (Arg a : args) { + op += " " + a; + } + return op + ")"; + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java new file mode 100644 index 0000000..cf12e3a --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/FilterParser.java @@ -0,0 +1,131 @@ +/** + * 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. + */ +package org.apache.zookeeper.graph; + +import java.io.PushbackReader; +import java.io.StringReader; +import java.io.IOException; +import java.util.ArrayList; + +import org.apache.zookeeper.graph.filterops.*; + +public class FilterParser { + private PushbackReader reader; + + public FilterParser(String s) { + reader = new PushbackReader(new StringReader(s)); + } + + private String readUntilSpace() throws IOException { + StringBuffer buffer = new StringBuffer(); + + int c = reader.read(); + while (!Character.isWhitespace(c) && c != ')' && c != '(') { + buffer.append((char)c); + c = reader.read(); + if (c == -1) { + break; + } + } + reader.unread(c); + + return buffer.toString().trim(); + } + + private StringArg readStringArg() throws IOException, FilterException { + int c = reader.read(); + int last = 0; + if (c != '"') { + throw new FilterException("Check the parser, trying to read a string that doesn't begin with quotes"); + } + StringBuffer buffer = new StringBuffer(); + while (reader.ready()) { + last = c; + c = reader.read(); + if (c == -1) { + break; + } + + if (c == '"' && last != '\\') { + return new StringArg(buffer.toString()); + } else { + buffer.append((char)c); + } + } + throw new FilterException("Unterminated string"); + } + + private NumberArg readNumberArg() throws IOException, FilterException { + String strval = readUntilSpace(); + + try { + if (strval.startsWith("0x")) { + return new NumberArg(Long.valueOf(strval.substring(2), 16)); + } else { + return new NumberArg(Long.valueOf(strval)); + } + } catch (NumberFormatException e) { + throw new FilterException("Not a number [" + strval + "]\n" + e); + } + } + + private SymbolArg readSymbolArg() throws IOException, FilterException { + return new SymbolArg(readUntilSpace()); + } + + public FilterOp parse() throws IOException, FilterException { + int c = reader.read(); + if (c != '(') { + throw new FilterException("Invalid format"); + } + + String opstr = readUntilSpace(); + FilterOp op = FilterOp.newOp(opstr); + + while (reader.ready()) { + c = reader.read(); + if (c == -1) { + break; + } + if (c == '(') { + reader.unread(c); + op.addSubOp(parse()); + } else if (c == ')') { + return op; + } else if (c == '"') { + reader.unread(c); + op.addArg(readStringArg()); + } else if (Character.isDigit(c) || c == '-' || c == '+') { + reader.unread(c); + op.addArg(readNumberArg()); + } else if (Character.isJavaIdentifierStart(c)) { + reader.unread(c); + op.addArg(readSymbolArg()); + } + } + throw new FilterException("Incomplete filter"); + } + + public static void main(String[] args) throws IOException, FilterException { + if (args.length == 1) { + System.out.println(new FilterParser(args[0]).parse()); + } else { + System.out.println(new FilterParser("(or (and (= session foobar) (= session barfoo)) (= session sdfs))").parse()); + } + } +}; http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java new file mode 100644 index 0000000..afaf3a1 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/JsonGenerator.java @@ -0,0 +1,223 @@ +/** + * 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. + */ +package org.apache.zookeeper.graph; + + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +import java.io.Writer; +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; + +public class JsonGenerator { + private JSONObject root; + private HashSet<Integer> servers; + + private class Message { + private int from; + private int to; + private long zxid; + + public Message(int from, int to, long zxid) { + this.from = from; + this.to = to; + this.zxid = zxid; + } + + public boolean equals(Message m) { + return (m.from == this.from + && m.to == this.to + && m.zxid == this.zxid); + } + }; + + public JSONObject txnEntry(TransactionEntry e) { + JSONObject event = new JSONObject(); + + event.put("time", Long.toString(e.getTimestamp())); + event.put("client", Long.toHexString(e.getClientId())); + event.put("cxid", Long.toHexString(e.getCxid())); + event.put("zxid", Long.toHexString(e.getZxid())); + event.put("op", e.getOp()); + event.put("extra", e.getExtra()); + event.put("type", "transaction"); + + return event; + } + + /** + Assumes entries are sorted by timestamp. + */ + public JsonGenerator(LogIterator iter) { + servers = new HashSet<Integer>(); + + Pattern stateChangeP = Pattern.compile("- (LOOKING|FOLLOWING|LEADING)"); + Pattern newElectionP = Pattern.compile("New election. My id = (\\d+), Proposed zxid = (\\d+)"); + Pattern receivedProposalP = Pattern.compile("Notification: (\\d+) \\(n.leader\\), (\\d+) \\(n.zxid\\), (\\d+) \\(n.round\\), .+ \\(n.state\\), (\\d+) \\(n.sid\\), .+ \\(my state\\)"); + Pattern exceptionP = Pattern.compile("xception"); + + root = new JSONObject(); + Matcher m = null; + JSONArray events = new JSONArray(); + root.put("events", events); + + long starttime = Long.MAX_VALUE; + long endtime = 0; + + int leader = 0; + long curEpoch = 0; + boolean newEpoch = false; + + while (iter.hasNext()) { + LogEntry ent = iter.next(); + + if (ent.getTimestamp() < starttime) { + starttime = ent.getTimestamp(); + } + if (ent.getTimestamp() > endtime) { + endtime = ent.getTimestamp(); + } + + if (ent.getType() == LogEntry.Type.TXN) { + events.add(txnEntry((TransactionEntry)ent)); + } else { + Log4JEntry e = (Log4JEntry)ent; + servers.add(e.getNode()); + + if ((m = stateChangeP.matcher(e.getEntry())).find()) { + JSONObject stateChange = new JSONObject(); + stateChange.put("type", "stateChange"); + stateChange.put("time", e.getTimestamp()); + stateChange.put("server", e.getNode()); + stateChange.put("state", m.group(1)); + events.add(stateChange); + + if (m.group(1).equals("LEADING")) { + leader = e.getNode(); + } + } else if ((m = newElectionP.matcher(e.getEntry())).find()) { + Iterator<Integer> iterator = servers.iterator(); + long zxid = Long.valueOf(m.group(2)); + int count = (int)zxid;// & 0xFFFFFFFFL; + int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; + + if (leader != 0 && epoch > curEpoch) { + JSONObject stateChange = new JSONObject(); + stateChange.put("type", "stateChange"); + stateChange.put("time", e.getTimestamp()); + stateChange.put("server", leader); + stateChange.put("state", "INIT"); + events.add(stateChange); + leader = 0; + } + + while (iterator.hasNext()) { + int dst = iterator.next(); + if (dst != e.getNode()) { + JSONObject msg = new JSONObject(); + msg.put("type", "postmessage"); + msg.put("src", e.getNode()); + msg.put("dst", dst); + msg.put("time", e.getTimestamp()); + msg.put("zxid", m.group(2)); + msg.put("count", count); + msg.put("epoch", epoch); + + events.add(msg); + } + } + } else if ((m = receivedProposalP.matcher(e.getEntry())).find()) { + // Pattern.compile("Notification: \\d+, (\\d+), (\\d+), \\d+, [^,]*, [^,]*, (\\d+)");//, LOOKING, LOOKING, 2 + int src = Integer.valueOf(m.group(4)); + long zxid = Long.valueOf(m.group(2)); + int dst = e.getNode(); + long epoch2 = Long.valueOf(m.group(3)); + + int count = (int)zxid;// & 0xFFFFFFFFL; + int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; + + if (leader != 0 && epoch > curEpoch) { + JSONObject stateChange = new JSONObject(); + stateChange.put("type", "stateChange"); + stateChange.put("time", e.getTimestamp()); + stateChange.put("server", leader); + stateChange.put("state", "INIT"); + events.add(stateChange); + leader = 0; + } + + if (src != dst) { + JSONObject msg = new JSONObject(); + msg.put("type", "delivermessage"); + msg.put("src", src); + msg.put("dst", dst); + msg.put("time", e.getTimestamp()); + msg.put("zxid", zxid); + msg.put("epoch", epoch); + msg.put("count", count); + msg.put("epoch2", epoch2); + + events.add(msg); + } + } else if ((m = exceptionP.matcher(e.getEntry())).find()) { + JSONObject ex = new JSONObject(); + ex.put("type", "exception"); + ex.put("server", e.getNode()); + ex.put("time", e.getTimestamp()); + ex.put("text", e.getEntry()); + events.add(ex); + } + } + JSONObject ex = new JSONObject(); + ex.put("type", "text"); + ex.put("time", ent.getTimestamp()); + String txt = ent.toString(); + ex.put("text", txt); + events.add(ex); + } + // System.out.println("pending messages: "+pendingMessages.size()); + root.put("starttime", starttime); + root.put("endtime", endtime); + + JSONArray serversarray = new JSONArray(); + root.put("servers", serversarray); + + Iterator<Integer> iterator = servers.iterator(); + while (iterator.hasNext()) { + serversarray.add(iterator.next()); + } + } + + public String toString() { + return JSONValue.toJSONString(root); + } + + public static void main(String[] args) throws Exception { + MergedLogSource src = new MergedLogSource(args); + LogIterator iter = src.iterator(); + System.out.println(new JsonGenerator(iter)); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/63aaf0a1/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java ---------------------------------------------------------------------- diff --git a/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java new file mode 100644 index 0000000..0edc146 --- /dev/null +++ b/zookeeper-contrib/zookeeper-contrib-loggraph/src/main/java/org/apache/zookeeper/graph/Log4JEntry.java @@ -0,0 +1,40 @@ +/** + * 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. + */ +package org.apache.zookeeper.graph; + +public class Log4JEntry extends LogEntry { + public Log4JEntry(long timestamp, int node, String entry) { + super(timestamp); + setAttribute("log-text", entry); + setAttribute("node", new Integer(node)); + } + + public String getEntry() { + return (String) getAttribute("log-text"); + } + + public String toString() { + return "" + getTimestamp() + "::::" + getNode() + "::::" + getEntry(); + } + + public int getNode() { + return (Integer) getAttribute("node"); + } + + public Type getType() { return LogEntry.Type.LOG4J; } +}
