Repository: bigtop
Updated Branches:
  refs/heads/master 41eecb9a1 -> 89d3ac480


BIGTOP-2476 Add Zookeeper Charm (closes #129)

Signed-off-by: Kevin W Monroe <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/bigtop/repo
Commit: http://git-wip-us.apache.org/repos/asf/bigtop/commit/89d3ac48
Tree: http://git-wip-us.apache.org/repos/asf/bigtop/tree/89d3ac48
Diff: http://git-wip-us.apache.org/repos/asf/bigtop/diff/89d3ac48

Branch: refs/heads/master
Commit: 89d3ac480ad38cd4006825873eb5534949721ffd
Parents: 41eecb9
Author: Pete Vander Giessen <[email protected]>
Authored: Mon Jun 6 10:46:48 2016 -0400
Committer: Kevin W Monroe <[email protected]>
Committed: Mon Oct 10 17:01:32 2016 -0500

----------------------------------------------------------------------
 .../charm/zookeeper/layer-zookeeper/README.md   | 192 ++++++
 .../zookeeper/layer-zookeeper/actions.yaml      |   4 +
 .../zookeeper/layer-zookeeper/actions/restart   |  42 ++
 .../layer-zookeeper/actions/smoke-test          |  43 ++
 .../charm/zookeeper/layer-zookeeper/config.yaml |  10 +
 .../charm/zookeeper/layer-zookeeper/copyright   |  16 +
 .../charm/zookeeper/layer-zookeeper/icon.svg    | 639 +++++++++++++++++++
 .../charm/zookeeper/layer-zookeeper/layer.yaml  |  15 +
 .../lib/charms/layer/zookeeper.py               | 188 ++++++
 .../zookeeper/layer-zookeeper/metadata.yaml     |  16 +
 .../layer-zookeeper/reactive/zookeeper.py       | 237 +++++++
 .../zookeeper/layer-zookeeper/tests/01-deploy   |  74 +++
 .../layer-zookeeper/tests/10-bind-address       | 115 ++++
 .../zookeeper/layer-zookeeper/tests/tests.yaml  |   3 +
 14 files changed, 1594 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md
new file mode 100644
index 0000000..69aa650
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/README.md
@@ -0,0 +1,192 @@
+<!--
+  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.
+-->
+# Overview
+
+Apache ZooKeeper is a high-performance coordination service for distributed
+applications. It exposes common services such as naming, configuration
+management, synchronization, and group services in a simple interface. Use it
+off-the-shelf to implement consensus, group management, leader election, and
+presence protocols.
+
+This charm provides the Zookeeper component of the [Apache Bigtop][] platform.
+
+[Apache Bigtop]: http://bigtop.apache.org/
+
+
+# Deploying
+
+A working Juju installation is assumed to be present. If Juju is not yet set
+up, please follow the [getting-started][] instructions prior to deploying this
+charm.
+
+Deploy a Zookeeper unit. With only one unit, the application will be running in
+`standalone` mode:
+
+    juju deploy zookeeper
+
+## Network-Restricted Environments
+Charms can be deployed in environments with limited network access. To deploy
+in this environment, configure a Juju model with appropriate proxy and/or
+mirror options. See [Configuring Models][] for more information.
+
+[getting-started]: https://jujucharms.com/docs/stable/getting-started
+[Configuring Models]: https://jujucharms.com/docs/stable/models-config
+
+## Configuring Network Interfaces
+In some network environments, zookeeper may need to be restricted to
+listen for incoming connections on a specific network interface
+(e.g.: for security reasons). To do so, configure zookeeper with either a
+network interface name or a CIDR range specifying a subnet. For example:
+
+    juju config zookeeper network_interface=eth0
+    juju config zookeeper network_interface=10.0.2.0/24
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju set-config zookeeper network_interface=eth0`.
+
+Each zookeeper unit in the cluster will lookup the IP address of that
+network interface, or find the first network interface with an IP
+address in the specified subnet, and bind Zookeeper to that address.
+
+If a mistake is made and an invalid name for the network interface is
+configured, recover by re-configuring with the correct name and then
+run "juju resolved" on any failed units:
+
+    juju config zookeeper network_interface=eth0
+    juju resolved zookeeper/0
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju set-config zookeeper network_interface=eth0;
+juju resolved -r zookeeper/0`.
+
+To go back to listening on all interfaces, configure zookeeper with
+`network_interface=0.0.0.0`:
+
+    juju config zookeeper network_interface=0.0.0.0
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju set-config zookeeper network_interface=0.0.0.0`.
+
+
+# Verifying
+
+## Status
+Apache Bigtop charms provide extended status reporting to indicate when they
+are ready:
+
+    juju status
+
+This is particularly useful when combined with `watch` to track the on-going
+progress of the deployment:
+
+    watch -n 2 juju status
+
+The message column will provide information about a given unit's state.
+This charm is ready for use once the status message indicates that it is
+ready.
+
+## Smoke Test
+This charm provides a `smoke-test` action that can be used to verify the
+application is functioning as expected. Run the action as follows:
+
+    juju run-action zookeeper/0 smoke-test
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action do zookeeper/0 smoke-test`.
+
+Watch the progress of the smoke test actions with:
+
+    watch -n 2 juju show-action-status
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action status`.
+
+Eventually, the action should settle to `status: completed`.  If it
+reports `status: failed`, the application is not working as expected. Get
+more information about a specific smoke test with:
+
+    juju show-action-output <action-id>
+
+> **Note**: The above assumes Juju 2.0 or greater. If using an earlier version
+of Juju, the syntax is `juju action fetch <action-id>`.
+
+## Utilities
+This charm includes Zookeeper command line utilities that can also be used to
+verify that the application is running as expected. Check the status of the
+Zookeeper daemon with `zkServer.sh`:
+
+    juju run --application=zookeeper '/usr/lib/zookeeper/bin/zkServer.sh 
status'
+
+A successful deployment will report the service mode as either `standalone`
+(if only one Zookeeper unit has been deployed) or `leader` / `follower` (if
+a Zookeeper quorum has been formed).
+
+
+# Scaling
+
+Running ZooKeeper in `standalone` mode is convenient for evaluation, some
+development, and testing. In production, however, ZooKeeper should be run in
+`replicated` mode. A replicated group of servers in the same application is
+called a quorum, and in `replicated` mode, all servers in the quorum have
+copies of the same configuration file.
+
+In order to add new Zookeeper servers to the quorum, simply add more units.
+For example, add two more zookeeper units with:
+
+    juju add-unit -n 2 zookeeper
+
+The Zookeeper nodes will automatically perform a rolling restart to update the
+Zookeeper quorum without losing any jobs in progress. Once the rolling restart
+has completed, all of the Zookeeper nodes should report the following status:
+
+    ready (n zk nodes)
+
+(Where 'n' is the total number of Zookeeper nodes in your quorum.)
+
+
+# Integrating
+
+To integrate Zookeeper into solutions with other charms, update the charms
+that require Zookeeper as follows:
+
+1) Add following lines to `metadata.yaml`:
+
+    requires:
+      zookeeper:
+         interface: zookeeper
+
+2) Add a `zookeeper-relation-changed` hook. Example contents:
+
+    from charmhelpers.core.hookenv import relation_get
+    ZK_hostname = relation_get('private-address')
+    ZK_port = relation_get('port')
+
+
+# Contact Information
+
+- <[email protected]>
+
+
+# Resources
+
+- [Apache Bigtop](http://bigtop.apache.org/) home page
+- [Apache Bigtop mailing lists](http://bigtop.apache.org/mail-lists.html)
+- [Apache Zookeeper home page](https://zookeeper.apache.org/)
+- [Apache Zookeeper issue 
tracker](https://issues.apache.org/jira/browse/ZOOKEEPER)
+- [Juju Bigtop charms](https://jujucharms.com/q/apache/bigtop)
+- [Juju mailing list](https://lists.ubuntu.com/mailman/listinfo/juju)
+- [Juju community](https://jujucharms.com/community)

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions.yaml 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions.yaml
new file mode 100644
index 0000000..a4e110f
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions.yaml
@@ -0,0 +1,4 @@
+restart:
+    description: Restart the Zookeeper server daemon.
+smoke-test:
+    description: Run an Apache Bigtop smoke test.

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart
new file mode 100755
index 0000000..4dcf6ae
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/restart
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+# 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 sys
+sys.path.append('lib')  # Add our helpers to our path.
+
+from charmhelpers.core import hookenv
+from charms.layer.zookeeper import Zookeeper
+from charms.reactive import is_state
+
+LOG = hookenv.log
+
+LOG("starting restart handler.")
+
+def main():
+    if not is_state('zookeeper.started'):
+        hookenv.action_fail('Cannot restart: Zookeeper has not yet started!')
+        return
+
+    LOG("starting restart handler main.")
+    hookenv.status_set('maintenance', 'restarting ...')
+    zookeeper = Zookeeper()
+    zookeeper.install()
+    hookenv.status_set('active', 'ready {}'.format(zookeeper.quorum_check()))
+
+
+if __name__ == '__main__':
+    main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test
new file mode 100755
index 0000000..37264cf
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/actions/smoke-test
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+# 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 sys
+sys.path.append('lib')
+
+from charmhelpers.core import hookenv
+from charms.layer.apache_bigtop_base import Bigtop
+from charms.reactive import is_state
+
+
+def fail(msg, output=None):
+    if output:
+        hookenv.action_set({'output': output})
+    hookenv.action_fail(msg)
+    sys.exit()
+
+if not is_state('zookeeper.started'):
+    fail('Charm is not yet ready to run the Bigtop smoke test(s)')
+
+# Bigtop smoke test components
+smoke_components = ['zookeeper']
+
+bigtop = Bigtop()
+result = bigtop.run_smoke_tests(smoke_components)
+if result == 'success':
+    hookenv.action_set({'outcome': result})
+else:
+    fail('{} smoke tests failed'.format(smoke_components), result)

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/config.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/config.yaml 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/config.yaml
new file mode 100644
index 0000000..d88a94b
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/config.yaml
@@ -0,0 +1,10 @@
+options:
+  network_interface:
+    default: ""
+    type: string
+    description: |
+      Network interface to bind the Zookeeper client port to. Defaults
+      to accepting connections on all interfaces. Accepts either the
+      name of an interface (e.g., 'eth0'), or a CIDR range. If the
+      latter, we\'ll bind to the first interface that we find with an
+      IP address in that range.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/copyright
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/copyright 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/copyright
new file mode 100644
index 0000000..e900b97
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/copyright
@@ -0,0 +1,16 @@
+Format: http://dep.debian.net/deps/dep5/
+
+Files: *
+Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved.
+License: Apache License 2.0
+ Licensed 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/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/icon.svg
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/icon.svg 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/icon.svg
new file mode 100644
index 0000000..4fd8c0c
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/icon.svg
@@ -0,0 +1,639 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="96"
+   height="96"
+   id="svg6517"
+   version="1.1"
+   inkscape:version="0.91+devel r"
+   sodipodi:docname="hw_zoo-keeper.svg">
+  <defs
+     id="defs6519">
+    <linearGradient
+       id="linearGradient4265"
+       inkscape:collect="always">
+      <stop
+         id="stop4267"
+         offset="0"
+         style="stop-color:#ab784f;stop-opacity:1" />
+      <stop
+         id="stop4269"
+         offset="1"
+         style="stop-color:#ab784f;stop-opacity:0" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4259"
+       inkscape:collect="always">
+      <stop
+         id="stop4261"
+         offset="0"
+         style="stop-color:#ffffff;stop-opacity:1" />
+      <stop
+         id="stop4263"
+         offset="1"
+         style="stop-color:#ffffff;stop-opacity:0" />
+    </linearGradient>
+    <inkscape:path-effect
+       effect="envelope"
+       id="path-effect4253"
+       is_visible="true"
+       yy="true"
+       xx="true"
+       bendpath1="m -215.76126,697.1481 139.143385,0"
+       bendpath2="m -76.617875,697.1481 0,90.93889"
+       bendpath3="m -215.76126,788.08699 139.143385,0"
+       bendpath4="m -215.76126,697.1481 0,90.93889" />
+    <inkscape:path-effect
+       effect="bend_path"
+       id="path-effect4251"
+       is_visible="true"
+       bendpath="m -215.76126,742.61754 139.143385,0"
+       prop_scale="1.12"
+       scale_y_rel="false"
+       vertical="false" />
+    <linearGradient
+       id="Background">
+      <stop
+         id="stop4178"
+         offset="0"
+         style="stop-color:#22779e;stop-opacity:1" />
+      <stop
+         id="stop4180"
+         offset="1"
+         style="stop-color:#2991c0;stop-opacity:1" />
+    </linearGradient>
+    <filter
+       style="color-interpolation-filters:sRGB;"
+       inkscape:label="Inner Shadow"
+       id="filter1121">
+      <feFlood
+         flood-opacity="0.59999999999999998"
+         flood-color="rgb(0,0,0)"
+         result="flood"
+         id="feFlood1123" />
+      <feComposite
+         in="flood"
+         in2="SourceGraphic"
+         operator="out"
+         result="composite1"
+         id="feComposite1125" />
+      <feGaussianBlur
+         in="composite1"
+         stdDeviation="1"
+         result="blur"
+         id="feGaussianBlur1127" />
+      <feOffset
+         dx="0"
+         dy="2"
+         result="offset"
+         id="feOffset1129" />
+      <feComposite
+         in="offset"
+         in2="SourceGraphic"
+         operator="atop"
+         result="composite2"
+         id="feComposite1131" />
+    </filter>
+    <filter
+       style="color-interpolation-filters:sRGB;"
+       inkscape:label="Drop Shadow"
+       id="filter950">
+      <feFlood
+         flood-opacity="0.25"
+         flood-color="rgb(0,0,0)"
+         result="flood"
+         id="feFlood952" />
+      <feComposite
+         in="flood"
+         in2="SourceGraphic"
+         operator="in"
+         result="composite1"
+         id="feComposite954" />
+      <feGaussianBlur
+         in="composite1"
+         stdDeviation="1"
+         result="blur"
+         id="feGaussianBlur956" />
+      <feOffset
+         dx="0"
+         dy="1"
+         result="offset"
+         id="feOffset958" />
+      <feComposite
+         in="SourceGraphic"
+         in2="offset"
+         operator="over"
+         result="composite2"
+         id="feComposite960" />
+    </filter>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath873">
+      <g
+         transform="matrix(0,-0.66666667,0.66604479,0,-258.25992,677.00001)"
+         id="g875"
+         inkscape:label="Layer 1"
+         style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline">
+        <path
+           style="fill:#ff00ff;fill-opacity:1;stroke:none;display:inline"
+           d="m 46.702703,898.22775 50.594594,0 C 138.16216,898.22775 
144,904.06497 144,944.92583 l 0,50.73846 c 0,40.86071 -5.83784,46.69791 
-46.702703,46.69791 l -50.594594,0 C 5.8378378,1042.3622 0,1036.525 0,995.66429 
L 0,944.92583 C 0,904.06497 5.8378378,898.22775 46.702703,898.22775 Z"
+           id="path877"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="sssssssss" />
+      </g>
+    </clipPath>
+    <filter
+       inkscape:collect="always"
+       id="filter891"
+       inkscape:label="Badge Shadow">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.71999962"
+         id="feGaussianBlur893" />
+    </filter>
+    <style
+       id="style867"
+       type="text/css"><![CDATA[
+    .fil0 {fill:#1F1A17}
+   ]]></style>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4439"
+       id="linearGradient908"
+       x1="-220"
+       y1="731.29077"
+       x2="-220"
+       y2="635.29077"
+       gradientUnits="userSpaceOnUse" />
+    <clipPath
+       id="clipPath16">
+      <path
+         id="path18"
+         d="m -9,-9 614,0 0,231 -614,0 0,-231 z" />
+    </clipPath>
+    <clipPath
+       id="clipPath116">
+      <path
+         id="path118"
+         d="m 91.7368,146.3253 -9.7039,-1.577 -8.8548,-3.8814 -7.5206,-4.7308 
-7.1566,-8.7335 -4.0431,-4.282 -3.9093,-1.4409 -1.034,2.5271 1.8079,2.6096 
0.4062,3.6802 1.211,-0.0488 1.3232,-1.2069 -0.3569,3.7488 -1.4667,0.9839 
0.0445,1.4286 -3.4744,-1.9655 -3.1462,-3.712 -0.6559,-3.3176 1.3453,-2.6567 
1.2549,-4.5133 2.5521,-1.2084 2.6847,0.1318 2.5455,1.4791 -1.698,-8.6122 
1.698,-9.5825 -1.8692,-4.4246 -6.1223,-6.5965 1.0885,-3.941 2.9002,-4.5669 
5.4688,-3.8486 2.9007,-0.3969 3.225,-0.1094 -2.012,-8.2601 7.3993,-3.0326 
9.2188,-1.2129 3.1535,2.0619 0.2427,5.5797 3.5178,5.8224 0.2426,4.6094 
8.4909,-0.6066 7.8843,0.7279 -7.8843,-4.7307 1.3343,-5.701 4.9731,-7.763 
4.8521,-2.0622 3.8814,1.5769 1.577,3.1538 8.1269,6.1861 1.5769,-1.3343 
12.7363,-0.485 2.5473,2.0619 0.2426,3.6391 -0.849,1.5767 -0.6066,9.8251 
-4.2454,8.4909 0.7276,3.7605 2.5475,-1.3343 7.1566,-6.6716 3.5175,-0.2424 
3.8815,1.5769 3.8818,2.9109 1.9406,6.3077 11.4021,-0.7277 6.914,2.6686 
5.5797,5.2157 4.0028,7.5206 0.9706,8.8546
  -0.8493,10.3105 -2.1832,9.2185 -2.1836,2.9112 -3.0322,0.9706 -5.3373,-5.8224 
-4.8518,-1.6982 -4.2455,7.0353 -4.2454,3.8815 -2.3049,1.4556 -9.2185,7.6419 
-7.3993,4.0028 -7.3993,0.6066 -8.6119,-1.4556 -7.5206,-2.7899 -5.2158,-4.2454 
-4.1241,-4.9734 -4.2454,-1.2129" />
+    </clipPath>
+    <clipPath
+       id="clipPath128">
+      <path
+         id="path130"
+         d="m 91.7368,146.3253 -9.7039,-1.577 -8.8548,-3.8814 -7.5206,-4.7308 
-7.1566,-8.7335 -4.0431,-4.282 -3.9093,-1.4409 -1.034,2.5271 1.8079,2.6096 
0.4062,3.6802 1.211,-0.0488 1.3232,-1.2069 -0.3569,3.7488 -1.4667,0.9839 
0.0445,1.4286 -3.4744,-1.9655 -3.1462,-3.712 -0.6559,-3.3176 1.3453,-2.6567 
1.2549,-4.5133 2.5521,-1.2084 2.6847,0.1318 2.5455,1.4791 -1.698,-8.6122 
1.698,-9.5825 -1.8692,-4.4246 -6.1223,-6.5965 1.0885,-3.941 2.9002,-4.5669 
5.4688,-3.8486 2.9007,-0.3969 3.225,-0.1094 -2.012,-8.2601 7.3993,-3.0326 
9.2188,-1.2129 3.1535,2.0619 0.2427,5.5797 3.5178,5.8224 0.2426,4.6094 
8.4909,-0.6066 7.8843,0.7279 -7.8843,-4.7307 1.3343,-5.701 4.9731,-7.763 
4.8521,-2.0622 3.8814,1.5769 1.577,3.1538 8.1269,6.1861 1.5769,-1.3343 
12.7363,-0.485 2.5473,2.0619 0.2426,3.6391 -0.849,1.5767 -0.6066,9.8251 
-4.2454,8.4909 0.7276,3.7605 2.5475,-1.3343 7.1566,-6.6716 3.5175,-0.2424 
3.8815,1.5769 3.8818,2.9109 1.9406,6.3077 11.4021,-0.7277 6.914,2.6686 
5.5797,5.2157 4.0028,7.5206 0.9706,8.8546
  -0.8493,10.3105 -2.1832,9.2185 -2.1836,2.9112 -3.0322,0.9706 -5.3373,-5.8224 
-4.8518,-1.6982 -4.2455,7.0353 -4.2454,3.8815 -2.3049,1.4556 -9.2185,7.6419 
-7.3993,4.0028 -7.3993,0.6066 -8.6119,-1.4556 -7.5206,-2.7899 -5.2158,-4.2454 
-4.1241,-4.9734 -4.2454,-1.2129" />
+    </clipPath>
+    <linearGradient
+       id="linearGradient3850"
+       inkscape:collect="always">
+      <stop
+         id="stop3852"
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;" />
+      <stop
+         id="stop3854"
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath3095">
+      <path
+         d="m 976.648,389.551 -842.402,0 0,839.999 842.402,0 0,-839.999"
+         id="path3097"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <linearGradient
+       id="linearGradient4439"
+       inkscape:collect="always">
+      <stop
+         id="stop4441"
+         offset="0"
+         style="stop-color:#ddc1ab;stop-opacity:1" />
+      <stop
+         id="stop4443"
+         offset="1"
+         style="stop-color:#edddd1;stop-opacity:1" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath3195">
+      <path
+         d="m 611.836,756.738 -106.34,105.207 c -8.473,8.289 -13.617,20.102 
-13.598,33.379 L 598.301,790.207 c -0.031,-13.418 5.094,-25.031 13.535,-33.469"
+         id="path3197"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath3235">
+      <path
+         d="m 1095.64,1501.81 c 35.46,-35.07 70.89,-70.11 106.35,-105.17 
4.4,-4.38 7.11,-10.53 7.11,-17.55 l -106.37,105.21 c 0,7 -2.71,13.11 
-7.09,17.51"
+         id="path3237"
+         inkscape:connector-curvature="0" />
+    </clipPath>
+    <clipPath
+       id="clipPath4591"
+       clipPathUnits="userSpaceOnUse">
+      <path
+         inkscape:connector-curvature="0"
+         d="m 1106.6009,730.43734 -0.036,21.648 c -0.01,3.50825 
-2.8675,6.61375 -6.4037,6.92525 l -83.6503,7.33162 c -3.5205,0.30763 
-6.3812,-2.29987 -6.3671,-5.8145 l 0.036,-21.6475 20.1171,-1.76662 
-0.011,4.63775 c 0,1.83937 1.4844,3.19925 3.3262,3.0395 l 49.5274,-4.33975 c 
1.8425,-0.166 3.3425,-1.78125 3.3538,-3.626 l 0.01,-4.63025 20.1,-1.7575"
+         style="fill:#ff00ff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+         id="path4593" />
+    </clipPath>
+    <radialGradient
+       gradientUnits="userSpaceOnUse"
+       
gradientTransform="matrix(-1.4333926,-2.2742838,1.1731823,-0.73941125,-174.08025,98.374394)"
+       r="20.40658"
+       fy="93.399292"
+       fx="-26.508606"
+       cy="93.399292"
+       cx="-26.508606"
+       id="radialGradient3856"
+       xlink:href="#linearGradient3850"
+       inkscape:collect="always" />
+    <linearGradient
+       gradientTransform="translate(-318.48033,212.32022)"
+       gradientUnits="userSpaceOnUse"
+       y2="993.19702"
+       x2="-51.879555"
+       y1="593.11615"
+       x1="348.20132"
+       id="linearGradient3895"
+       xlink:href="#linearGradient3850"
+       inkscape:collect="always" />
+    <clipPath
+       id="clipPath3906"
+       clipPathUnits="userSpaceOnUse">
+      <rect
+         transform="scale(1,-1)"
+         
style="opacity:0.8;color:#000000;fill:#ff00ff;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect3908"
+         width="1019.1371"
+         height="1019.1371"
+         x="357.9816"
+         y="-1725.8152" />
+    </clipPath>
+    <filter
+       style="color-interpolation-filters:sRGB"
+       inkscape:label="Inner Shadow"
+       id="filter1121-7">
+      <feFlood
+         flood-opacity="0.59999999999999998"
+         flood-color="rgb(0,0,0)"
+         result="flood"
+         id="feFlood1123-8" />
+      <feComposite
+         in="flood"
+         in2="SourceGraphic"
+         operator="out"
+         result="composite1"
+         id="feComposite1125-4" />
+      <feGaussianBlur
+         in="composite1"
+         stdDeviation="1"
+         result="blur"
+         id="feGaussianBlur1127-8" />
+      <feOffset
+         dx="0"
+         dy="2"
+         result="offset"
+         id="feOffset1129-7" />
+      <feComposite
+         in="offset"
+         in2="SourceGraphic"
+         operator="atop"
+         result="composite2"
+         id="feComposite1131-8" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4259"
+       id="linearGradient4293"
+       x1="61.724815"
+       y1="60.486755"
+       x2="105.60001"
+       y2="48.000011"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4265"
+       id="linearGradient4301"
+       x1="57.529388"
+       y1="52.851879"
+       x2="-5.595726"
+       y2="70.892845"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4265"
+       id="linearGradient4307"
+       gradientUnits="userSpaceOnUse"
+       x1="57.529388"
+       y1="52.851879"
+       x2="-5.595726"
+       y2="70.892845"
+       
gradientTransform="matrix(-0.12987249,0.9915307,-0.9915307,-0.12987249,112.98532,15.539208)"
 />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4259"
+       id="linearGradient4309"
+       gradientUnits="userSpaceOnUse"
+       x1="61.724815"
+       y1="60.486755"
+       x2="105.60001"
+       y2="48.000011"
+       
gradientTransform="matrix(-0.12987249,0.9915307,-0.9915307,-0.12987249,112.98532,15.539208)"
 />
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4336">
+      <path
+         
style="display:inline;opacity:0.06300001;fill:#df382c;fill-opacity:1;stroke:none"
+         d="m -1.8530274e-8,64.86487 0,-33.72973 C -1.8530274e-8,3.8919 
3.8878496,1.038147e-5 31.10302,1.038147e-5 l 33.79408,0 C 92.11217,1.038147e-5 
96,3.8919 96,31.13514 l 0,33.72973 c 0,27.24325 -3.88783,31.13514 
-31.1029,31.13514 l -33.79408,0 C 3.8878496,96.00001 -1.8530274e-8,92.10812 
-1.8530274e-8,64.86487 Z"
+         id="path4338"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="sssssssss" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath4455">
+      <path
+         sodipodi:nodetypes="sssssssss"
+         inkscape:connector-curvature="0"
+         id="path4457"
+         d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 
31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 
0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 c 
-27.21517,0 -31.10302,-3.89189 -31.10302,-31.13514 z"
+         style="display:inline;fill:#df382c;fill-opacity:1;stroke:none" />
+    </clipPath>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="6.3664627"
+     inkscape:cx="27.87084"
+     inkscape:cy="45.622606"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer3"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1029"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     showborder="true"
+     showguides="false"
+     inkscape:guide-bbox="true"
+     inkscape:showpageshadow="false"
+     inkscape:snap-global="false"
+     inkscape:snap-bbox="true"
+     inkscape:bbox-paths="true"
+     inkscape:bbox-nodes="true"
+     inkscape:snap-bbox-edge-midpoints="true"
+     inkscape:snap-bbox-midpoints="true"
+     inkscape:object-paths="true"
+     inkscape:snap-intersection-paths="true"
+     inkscape:object-nodes="true"
+     inkscape:snap-smooth-nodes="true"
+     inkscape:snap-midpoints="true"
+     inkscape:snap-object-midpoints="true"
+     inkscape:snap-center="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid821" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="16,48"
+       id="guide823" />
+    <sodipodi:guide
+       orientation="0,1"
+       position="64,80"
+       id="guide825" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="80,40"
+       id="guide827" />
+    <sodipodi:guide
+       orientation="0,1"
+       position="64,16"
+       id="guide829" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata6522">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="BACKGROUND"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(268,-635.29076)"
+     style="display:inline"
+     sodipodi:insensitive="true">
+    <g
+       id="g4464"
+       style="filter:url(#filter1121)">
+      <path
+         sodipodi:nodetypes="sssssssss"
+         inkscape:connector-curvature="0"
+         id="path6455"
+         d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 
31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 
0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 c 
-27.21517,0 -31.10302,-3.89189 -31.10302,-31.13514 z"
+         
style="display:inline;fill:url(#linearGradient908);fill-opacity:1;stroke:none" 
/>
+    </g>
+    <path
+       sodipodi:nodetypes="sssssssss"
+       inkscape:connector-curvature="0"
+       id="path4460"
+       d="m -268,700.15563 0,-33.72973 c 0,-27.24324 3.88785,-31.13513 
31.10302,-31.13513 l 33.79408,0 c 27.21507,0 31.1029,3.89189 31.1029,31.13513 l 
0,33.72973 c 0,27.24325 -3.88783,31.13514 -31.1029,31.13514 l -33.79408,0 c 
-27.21517,0 -31.10302,-3.89189 -31.10302,-31.13514 z"
+       
style="display:inline;fill:none;fill-opacity:1;stroke:none;filter:url(#filter1121)"
 />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="FOLDS"
+     style="display:inline"
+     sodipodi:insensitive="true">
+    <g
+       inkscape:groupmode="maskhelper"
+       id="g4327"
+       clip-path="url(#clipPath4336)">
+      <g
+         id="g4319">
+        <g
+           id="g4311"
+           
transform="matrix(-0.76919808,0.63901044,-0.63901044,-0.76919808,108.56506,55.223802)"
+           style="opacity:0.10499998">
+          <path
+             
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4307);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+             d="M 118.8011,58.528402 -28.812125,79.242985 5.8755118,-40.074102 
l 96.0121782,20.991633 26.85965,50.05246 z"
+             id="path4303"
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="cccccc" />
+          <path
+             
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4309);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+             d="M 118.8011,58.528402 -28.812125,79.242985 -8.4409816,141.41578 
108.00471,151.91561 Z"
+             id="path4305"
+             inkscape:connector-curvature="0"
+             sodipodi:nodetypes="ccccc" />
+        </g>
+        <g
+           id="g4315"
+           style="opacity:0.08200001"
+           
transform="matrix(0.24294261,-0.68963031,1.0086859,1.2528869,10.128532,28.560155)">
+          <path
+             sodipodi:nodetypes="cccccc"
+             inkscape:connector-curvature="0"
+             id="path4283"
+             d="M 41.869796,-11.349643 81.579838,132.32315 -41.231687,113.4253 
-32.887179,15.500039 13.253042,-17.632567 Z"
+             
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4301);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
 />
+          <path
+             sodipodi:nodetypes="ccccc"
+             inkscape:connector-curvature="0"
+             id="path4285"
+             d="M 41.869796,-11.349643 81.579838,132.32315 140.58042,104.05 
135.86823,-12.773118 Z"
+             
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4293);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
 />
+        </g>
+      </g>
+    </g>
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="PLACE YOUR PICTOGRAM HERE"
+     style="display:inline">
+    <path
+       
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c39a7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-backgrou
 nd:accumulate"
+       d="M 17.371094 28.669922 C 16.599038 28.669922 15.566054 28.794985 
14.679688 29.570312 C 13.766682 30.36894 13.5 31.521747 13.5 32.382812 L 13.5 
34.671875 C 13.5 35.527049 13.783856 36.716545 14.75 37.482422 C 15.667283 
38.209564 16.626117 38.240234 17.234375 38.240234 L 22.816406 38.240234 L 
14.875 46.978516 A 0.50005 0.50005 0 0 0 14.871094 46.982422 L 14.869141 
46.984375 L 14.847656 47.007812 L 14.826172 47.029297 L 14.818359 47.039062 A 
0.50005 0.50005 0 0 0 14.701172 47.265625 C 13.864555 48.288569 13.5 49.604301 
13.5 50.416016 L 13.5 53.255859 C 13.5 54.169576 13.90884 55.262942 14.785156 
55.900391 C 15.592631 56.48776 16.397758 56.525391 16.84375 56.525391 L 
19.427734 56.525391 C 19.718486 56.524847 19.997357 56.437001 20.269531 
56.337891 C 20.541704 56.437001 20.820576 56.524847 21.111328 56.525391 L 
33.220703 56.525391 L 33.230469 56.525391 A 0.50005 0.50005 0 0 0 33.402344 
56.494141 C 34.12378 56.484341 34.962109 56.329895 35.681641 55.701172 C 
36.510768 54.976682 3
 6.785156 53.947547 36.785156 53.171875 L 36.785156 52.060547 L 39.943359 
55.21875 C 40.598634 55.880878 41.817199 56.525391 43.085938 56.525391 L 
51.427734 56.525391 C 51.65759 56.525312 51.884682 56.488241 52.105469 
56.425781 C 52.91232 56.579227 53.75153 56.381051 54.339844 55.792969 L 
57.271484 52.861328 A 0.50005 0.50005 0 0 0 57.273438 52.859375 L 58.769531 
51.34375 L 60.810547 53.228516 L 62.800781 55.21875 C 63.456055 55.880877 
64.674623 56.525391 65.943359 56.525391 L 74.285156 56.525391 C 74.373229 
56.525336 74.457796 56.496578 74.544922 56.486328 L 74.544922 56.296875 C 
74.544922 55.854004 74.900879 55.498047 75.34375 55.498047 L 77.492188 
55.498047 L 81.09375 51.898438 L 81.072266 51.912109 A 0.50005 0.50005 0 0 0 
81.126953 51.865234 C 81.887266 51.097737 82.5 49.757009 82.5 48.560547 L 82.5 
36.882812 A 0.50005 0.50005 0 0 0 82.5 36.880859 L 82.492188 35.5625 A 0.50005 
0.50005 0 0 0 82.490234 35.521484 C 82.439614 34.945574 82.188686 34.394202 
81.767578 33.972656 L 77.726
 562 29.933594 L 77.722656 29.927734 C 77.693286 29.897724 77.663652 29.868344 
77.632812 29.839844 A 0.50005 0.50005 0 0 0 77.626953 29.835938 C 76.880074 
29.164479 75.644158 28.669922 74.529297 28.669922 L 66.285156 28.669922 A 
0.50005 0.50005 0 0 0 66.283203 28.669922 L 65.035156 28.673828 A 0.50005 
0.50005 0 0 0 65.011719 28.673828 C 64.405147 28.705828 63.818351 28.959454 
63.375 29.402344 A 0.50005 0.50005 0 0 0 63.373047 29.404297 L 58.884766 
33.919922 L 58.164062 33.226562 L 54.869141 29.933594 L 54.865234 29.927734 C 
54.835864 29.897724 54.806231 29.868344 54.775391 29.839844 A 0.50005 0.50005 0 
0 0 54.769531 29.835938 C 54.022652 29.164479 52.786736 28.669922 51.671875 
28.669922 L 43.427734 28.669922 A 0.50005 0.50005 0 0 0 43.423828 28.669922 L 
42.101562 28.677734 A 0.50005 0.50005 0 0 0 42.060547 28.679688 C 41.486404 
28.731437 40.937815 28.982542 40.517578 29.402344 L 36.785156 33.136719 L 
36.785156 32.373047 C 36.785156 31.723134 36.686396 30.638195 35.828125 
29.746094 C 
 34.967253 28.851281 33.834956 28.669922 32.972656 28.669922 L 17.371094 
28.669922 z M 44.785156 38.240234 L 50.072266 38.240234 L 50.072266 46.955078 L 
44.785156 46.955078 L 44.785156 38.240234 z M 67.642578 38.240234 L 72.929688 
38.240234 L 72.929688 46.955078 L 67.642578 46.955078 L 67.642578 38.240234 z M 
28.535156 39.382812 L 28.810547 39.382812 L 18.5 51.277344 L 18.5 50.421875 C 
18.505 50.416575 18.510665 50.4116 18.515625 50.40625 A 0.50005 0.50005 0 0 0 
18.517578 50.402344 L 28.535156 39.382812 z M 35.214844 39.626953 L 35.214844 
47.595703 C 34.546422 47.177561 33.820937 46.955078 33.205078 46.955078 L 
28.863281 46.955078 L 35.214844 39.626953 z "
+       id="path4241" />
+    <rect
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#c39a7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="rect4282"
+       width="5.4180727"
+       height="1.5211427"
+       x="31.306763"
+       y="47.455326" />
+    <path
+       inkscape:connector-curvature="0"
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       d="M 17.371094,31.170159 C 16.41517,31.170159 16,31.519759 16,32.383049 
l 0,2.289063 c 0,0.843413 0.397114,1.068359 1.234375,1.068359 l 17.050781,0 
0,-3.367187 c 0,-0.783097 -0.249087,-1.203125 -1.3125,-1.203125 l -15.601562,0 
z m 24.914062,0 -3.960937,3.960937 c -0.46567,0.428278 -0.609375,0.86035 
-0.609375,1.65625 l 0,11.525391 4.570312,0 0,-17.142578 z m 1.142578,0 
0,4.570312 13.714844,0 -4.044922,-4.044922 c -0.437271,-0.393115 
-0.739999,-0.52539 -1.425781,-0.52539 l -8.244141,0 z m 21.714844,0 
-3.960937,3.960937 c -0.46567,0.428278 -0.609375,0.86035 -0.609375,1.65625 l 
0,11.525391 4.570312,0 0,-17.142578 z m 1.142578,0 0,4.570312 13.714844,0 
-4.044922,-4.044922 c -0.437271,-0.393115 -0.739999,-0.52539 -1.425781,-0.52539 
l -8.244141,0 z M 27.427734,36.883049 16.667969,48.72094 C 16.008587,49.402426 
16,49.71964 16,50.416252 l 0,2.839844 c 0,0.632516 0.402503,0.769531 
0.84375,0.769531 l 2.583984,0 14.857422,-17.142578 -6.857422,0 z m 25.144532,0 
0,17.142578 3.919922,-3.9199
 22 c 0.519261,-0.524371 0.65039,-0.777677 0.65039,-1.544921 l 0,-11.677735 
-4.570312,0 z m 22.857422,0 0,17.142578 3.919921,-3.919922 C 79.86887,49.581334 
80,49.328028 80,48.560784 l 0,-11.677735 -4.570312,0 z m -50.28711,12.572266 
-4.03125,4.570312 12.119141,0 c 0.745441,0.03464 1.054687,-0.303646 
1.054687,-0.853515 l 0,-2.6875 c 0,-0.653569 -0.291585,-1.029297 
-1.080078,-1.029297 l -8.0625,0 z m 12.572266,0 4.003906,4.003906 c 
0.359189,0.363486 0.690415,0.566406 1.367188,0.566406 l 8.341796,0 0,-4.570312 
-13.71289,0 z m 22.857422,0 4.003906,4.003906 c 0.359189,0.363486 
0.690414,0.566406 1.367187,0.566406 l 8.341797,0 0,-4.570312 -13.71289,0 z"
+       id="path4268" />
+    <path
+       
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:medium;line-height:125%;font-family:'Liberation
 Mono';-inkscape-font-specification:'Liberation Mono 
Bold';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#c39a7a;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 13.5,53.994141 0,12.99414 2.841797,0 0,-3.998047 
1.222656,-1.367187 3.111328,5.365234 3.351563,0 -4.613282,-7.417969 
4.429688,-5.576171 -3.302734,0 -4.199219,5.769531 0,-5.769531 z m 11.837891,0 
0,12.99414 9.71875,0 0,-2.197265 -6.876953,0 0,-3.283204 5.894531,0 0,-2.197265 
-5.894531,0 0,-3.121094 6.480468,0 0,-2.195312 z m 11.828125,0 0,12.99414 
9.716796,0 0,-2.197265 -6.876953,0 0,-3.283204 5.894532,0 0,-2.197265 
-5.894532,0 0,-3.121094 6.482422,0 0,-2.195312 z m 11.816406,0 0,12.99414 
2.841797,0 0,-4.585937 1.898437,0 c 0.854016,0 1.591991,-0.111197 
2.214844,-0.335938 0.622854,-0.231162 1.136482,-0.543855 1.541016,-0.935547 
0.404534,-0.39169 0.703849,-0.847072 0.896484,-1.367187 0.199057,-0.526536 
0.298828,-1.081689 0.298828,-1.666016 0,-0.635696 -0.103116,-1.207571 
-0.308594,-1.714844 -0.199056,-0.507272 -0.508405,-0.937851 -0.925781,-1.291015 
-0.417376,-0.353165 -0.942429,-0.622381 -1.578125,-0.808594 -0.635696,-0.192635 
-1.387048,-0.289062 -2.253906,-0.289062 z m 1
 1.83789,0 0,12.99414 9.71875,0 0,-2.197265 -6.876953,0 0,-3.283204 5.894532,0 
0,-2.197265 -5.894532,0 0,-3.121094 6.482422,0 0,-2.195312 z m 11.828126,0 
0,12.99414 2.841796,0 0,-4.960937 1.666016,0 2.792969,4.960937 3.197265,0 
-3.361328,-5.597656 c 0.391692,-0.115581 0.752599,-0.274991 1.080078,-0.480469 
0.32748,-0.211898 0.610073,-0.463 0.847657,-0.751953 0.237583,-0.295374 
0.423749,-0.636214 0.558593,-1.021484 0.134845,-0.385271 0.201172,-0.813896 
0.201172,-1.289063 0,-1.245706 -0.420545,-2.199947 -1.261718,-2.861328 
-0.841175,-0.661381 -2.109499,-0.992187 -3.804688,-0.992187 z m 
-20.824219,2.205078 1.464843,0 c 0.841174,0 1.469343,0.151329 1.886719,0.453125 
0.423798,0.295374 0.636719,0.794232 0.636719,1.49414 0,0.680644 
-0.196199,1.197618 -0.587891,1.550782 -0.391691,0.346743 -1.01178,0.519531 
-1.859375,0.519531 l -1.541015,0 z m 23.666015,0.263525 1.617188,0 c 0.398112,0 
0.75233,0.03345 1.060547,0.09766 0.314636,0.05779 0.577163,0.157563 
0.789062,0.298828 0.211899,0.134845 0.373
 262,0.314321 0.482422,0.539063 0.115581,0.22474 0.173828,0.368882 
0.173828,0.696362 0,0.60359 -0.192854,0.933898 -0.578125,1.254956 
-0.385269,0.314638 -1.002014,0.472657 -1.849609,0.472657 l -1.695313,0 z"
+       id="path4243"
+       inkscape:connector-curvature="0"
+       
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccscscscscsccccccccccccccccccccccccssssccscscscccscccscscc"
 />
+    <path
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#c39a7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       d="m 14,54.026463 21.32109,0 2.573485,0 2.959509,0 33.431633,0 
0,2.498928 -60.285717,0 z"
+       id="rect4314"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccc" />
+    <rect
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#c39a7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="rect4280"
+       width="1.4425607"
+       height="7.0013866"
+       x="13.5"
+       y="51.883324" />
+    <rect
+       
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#c39a7a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="rect4284"
+       width="0.93164003"
+       height="3.311523"
+       x="35.353516"
+       y="52.012886" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="BADGE"
+     style="display:none"
+     sodipodi:insensitive="true">
+    <g
+       style="display:inline"
+       transform="translate(-340.00001,-581)"
+       id="g4394"
+       clip-path="none">
+      <g
+         id="g855">
+        <g
+           inkscape:groupmode="maskhelper"
+           id="g870"
+           clip-path="url(#clipPath873)"
+           style="opacity:0.6;filter:url(#filter891)">
+          <path
+             transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-237.54282)"
+             d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 
0 0 1 12,-12 12,12 0 0 1 12,12 z"
+             sodipodi:ry="12"
+             sodipodi:rx="12"
+             sodipodi:cy="552.36218"
+             sodipodi:cx="252"
+             id="path844"
+             
style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             sodipodi:type="arc" />
+        </g>
+        <g
+           id="g862">
+          <path
+             sodipodi:type="arc"
+             
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             id="path4398"
+             sodipodi:cx="252"
+             sodipodi:cy="552.36218"
+             sodipodi:rx="12"
+             sodipodi:ry="12"
+             d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 
0 0 1 12,-12 12,12 0 0 1 12,12 z"
+             transform="matrix(1.4999992,0,0,1.4999992,-29.999795,-238.54282)" 
/>
+          <path
+             transform="matrix(1.25,0,0,1.25,33,-100.45273)"
+             d="m 264,552.36218 a 12,12 0 0 1 -12,12 12,12 0 0 1 -12,-12 12,12 
0 0 1 12,-12 12,12 0 0 1 12,12 z"
+             sodipodi:ry="12"
+             sodipodi:rx="12"
+             sodipodi:cy="552.36218"
+             sodipodi:cx="252"
+             id="path4400"
+             
style="color:#000000;fill:#dd4814;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             sodipodi:type="arc" />
+          <path
+             sodipodi:type="star"
+             
style="color:#000000;fill:#f5f5f5;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+             id="path4459"
+             sodipodi:sides="5"
+             sodipodi:cx="666.19574"
+             sodipodi:cy="589.50385"
+             sodipodi:r1="7.2431178"
+             sodipodi:r2="4.3458705"
+             sodipodi:arg1="1.0471976"
+             sodipodi:arg2="1.6755161"
+             inkscape:flatsided="false"
+             inkscape:rounded="0.1"
+             inkscape:randomized="0"
+             d="m 669.8173,595.77657 c -0.39132,0.22593 -3.62645,-1.90343 
-4.07583,-1.95066 -0.44938,-0.0472 -4.05653,1.36297 -4.39232,1.06062 
-0.3358,-0.30235 0.68963,-4.03715 0.59569,-4.47913 -0.0939,-0.44198 
-2.5498,-3.43681 -2.36602,-3.8496 0.18379,-0.41279 4.05267,-0.59166 
4.44398,-0.81759 0.39132,-0.22593 2.48067,-3.48704 2.93005,-3.4398 
0.44938,0.0472 1.81505,3.67147 2.15084,3.97382 0.3358,0.30236 4.08294,1.2817 
4.17689,1.72369 0.0939,0.44198 -2.9309,2.86076 -3.11469,3.27355 
-0.18379,0.41279 0.0427,4.27917 -0.34859,4.5051 z"
+             
transform="matrix(1.511423,-0.16366377,0.16366377,1.511423,-755.37346,-191.93651)"
 />
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/layer.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/layer.yaml 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/layer.yaml
new file mode 100644
index 0000000..7f6ee76
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/layer.yaml
@@ -0,0 +1,15 @@
+repo: 
https://github.com/apache/bigtop/tree/master/bigtop-packages/src/charm/zookeeper/layer-zookeeper
+includes:
+  - 'layer:apache-bigtop-base'
+  - 'layer:leadership'
+  - 'interface:zookeeper-quorum'
+  - 'interface:zookeeper'
+options:
+  apache-bigtop-base:
+    ports:
+      zookeeper-rest:
+          port: 9998
+          exposed_on: 'zookeeper'
+      zookeeper:
+          port: 2181
+          exposed_on: 'zookeeper'

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py
 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py
new file mode 100644
index 0000000..4b4932b
--- /dev/null
+++ 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/lib/charms/layer/zookeeper.py
@@ -0,0 +1,188 @@
+# 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 subprocess
+
+from charmhelpers.core import host
+from charmhelpers.core.hookenv import (open_port, close_port, log,
+                                       unit_private_ip, local_unit, config)
+from charms import layer
+from charms.layer.apache_bigtop_base import Bigtop
+from charms.reactive.relations import RelationBase
+from jujubigdata.utils import DistConfig
+
+
+def format_node(unit, node_ip):
+    '''
+    Given a juju unit name and an ip address, return a tuple
+    containing an id and formatted ip string suitable for passing to
+    puppet, which will write it out to zoo.cfg.
+
+    '''
+    return (unit.split("/")[1], "{ip}:2888:3888".format(ip=node_ip))
+
+
+class Zookeeper(object):
+    '''
+    Utility class for managing Zookeeper tasks like configuration, start,
+    stop, and adding and removing nodes.
+
+    '''
+    def __init__(self, dist_config=None):
+        self._dist_config = dist_config or DistConfig(
+            data=layer.options('apache-bigtop-base'))
+
+        self._roles = ['zookeeper-server', 'zookeeper-client']
+        self._hosts = {}
+
+    def is_zk_leader(self):
+        '''
+        Attempt to determine whether this node is the Zookeeper leader.
+
+        Note that Zookeeper tracks leadership independently of juju,
+        and that this command can fail, depending on the state that
+        the Zookeeper node is in when we attempt to run it.
+
+        '''
+        try:
+            status = subprocess.check_output(
+                ["/usr/lib/zookeeper/bin/zkServer.sh", "status"])
+            return "leader" in status.decode('utf-8')
+        except Exception:
+            log(
+                "Unable to determine whether this node is the Zookeeper 
leader.",
+                level="WARN"
+            )
+            return False
+
+    def read_peers(self):
+        '''
+        Fetch the list of peers available.
+
+        The first item in this list should always be the node that
+        this code is executing on.
+
+        '''
+        # A Zookeeper node likes to be first on the list.
+        nodes = [(local_unit(), unit_private_ip())]
+        # Get the list of peers
+        zkpeer = RelationBase.from_state('zkpeer.joined')
+        if zkpeer:
+            nodes.extend(sorted(zkpeer.get_nodes()))
+        nodes = [format_node(*node) for node in nodes]
+        return nodes
+
+    def sort_peers(self, zkpeer):
+        '''
+        Return peers, sorted in an order suitable for performing a rolling
+        restart.
+
+        '''
+        peers = self.read_peers()
+        leader = zkpeer.find_zk_leader()
+        peers.sort(key=lambda x: x[1] == leader)
+
+        return peers
+
+    @property
+    def dist_config(self):
+        '''
+        Charm level config.
+
+        '''
+        return self._dist_config
+
+    @property
+    def _override(self):
+        '''
+        Return a dict of keys and values that will override puppet's
+        defaults.
+
+        '''
+        override = {
+            "hadoop_zookeeper::server::myid": local_unit().split("/")[1],
+            "hadoop_zookeeper::server::ensemble": self.read_peers()
+        }
+        network_interface = config().get('network_interface')
+        if network_interface:
+            key = "hadoop_zookeeper::server::client_bind_addr"
+            override[key] = Bigtop().get_ip_for_interface(network_interface)
+
+        return override
+
+    def install(self, nodes=None):
+        '''
+        Write out the config, then run puppet.
+
+        After this runs, we should have a configured and running service.
+
+        '''
+        bigtop = Bigtop()
+        log("Rendering site yaml ''with overrides: {}".format(self._override))
+        bigtop.render_site_yaml(self._hosts, self._roles, self._override)
+        bigtop.trigger_puppet()
+        if self.is_zk_leader():
+            zkpeer = RelationBase.from_state('zkpeer.joined')
+            zkpeer.set_zk_leader()
+
+    def start(self):
+        '''
+        Request that our service start. Normally, puppet will handle this
+        for us.
+
+        '''
+        host.service_start('zookeeper-server')
+
+    def stop(self):
+        '''
+        Stop Zookeeper.
+
+        '''
+        host.service_stop('zookeeper-server')
+
+    def open_ports(self):
+        '''
+        Expose the ports in the configuration to the outside world.
+
+        '''
+        for port in self.dist_config.exposed_ports('zookeeper'):
+            open_port(port)
+
+    def close_ports(self):
+        '''
+        Close off communication from the outside world.
+
+        '''
+        for port in self.dist_config.exposed_ports('zookeeper'):
+            close_port(port)
+
+    def quorum_check(self):
+        '''
+        Returns a string reporting the node count. Append a message
+        informing the user if the node count is too low for good quorum,
+        or is even (meaning that one of the nodes is redundant for
+        quorum).
+
+        '''
+        node_count = len(self.read_peers())
+        if node_count == 1:
+            count_str = "{} zk node".format(node_count)
+        else:
+            count_str = "{} zk nodes".format(node_count)
+        if node_count < 3:
+            return " ({}; less than 3 nodes is suboptimal)".format(count_str)
+        if node_count % 2 == 0:
+            return " ({}; an even number is suboptimal)".format(count_str)
+        return "({})".format(count_str)

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/metadata.yaml
----------------------------------------------------------------------
diff --git a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/metadata.yaml 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/metadata.yaml
new file mode 100644
index 0000000..d9818d7
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/metadata.yaml
@@ -0,0 +1,16 @@
+name: zookeeper
+maintainer: Juju Big Data <[email protected]>
+summary: High-performance coordination service for distributed applications
+description: |
+  Apache ZooKeeper is a centralized, reliable application for maintaining
+  configuration information, naming, synchronization, and group services. All
+  of these kinds of services are used in some form or another by distributed
+  applications. In order to install and configure Apache HBase and other Hadoop
+  ecosystem components, you need ZooKeeper.
+tags: []
+provides:
+  zookeeper:
+    interface: zookeeper
+peers:
+  zkpeer:
+    interface: zookeeper-quorum

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py
new file mode 100644
index 0000000..825c219
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/reactive/zookeeper.py
@@ -0,0 +1,237 @@
+# 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 json
+import time
+from charmhelpers.core import hookenv
+from charms.layer.zookeeper import Zookeeper
+from charms.leadership import leader_set, leader_get
+from charms.reactive import set_state, when, when_not, is_state
+from charms.reactive.helpers import data_changed
+
+
+@when('bigtop.available')
+@when_not('zookeeper.installed')
+def install_zookeeper():
+    '''
+    After Bigtop has done the initial setup, trigger a puppet install,
+    via our Zooekeeper library.
+
+    puppet will start the service, as a side effect.
+
+    '''
+    hookenv.status_set('maintenance', 'installing zookeeper')
+    zookeeper = Zookeeper()
+    # Prime data changed
+    data_changed('zkpeer.nodes', zookeeper.read_peers())
+    data_changed(
+        'zk.network_interface',
+        hookenv.config().get('network_interface'))
+    zookeeper.install()
+    zookeeper.open_ports()
+    set_state('zookeeper.installed')
+    set_state('zookeeper.started')
+    hookenv.status_set('active', 'ready {}'.format(zookeeper.quorum_check()))
+
+
+def _restart_zookeeper(msg):
+    '''
+    Restart Zookeeper by re-running the puppet scripts.
+
+    '''
+    hookenv.status_set('maintenance', msg)
+    zookeeper = Zookeeper()
+    zookeeper.install()
+    hookenv.status_set('active', 'ready {}'.format(zookeeper.quorum_check()))
+
+
+@when('zookeeper.started')
+def update_network_interface():
+    '''
+    Possibly restart zookeeper, due to the network interface that it
+    should listen on changing.
+
+    '''
+    network_interface = hookenv.config().get('network_interface')
+    if data_changed('zk.network_interface', network_interface):
+        _restart_zookeeper('updating network interface')
+
+
+@when('zookeeper.started', 'zookeeper.joined')
+def serve_client(client):
+    config = Zookeeper().dist_config
+    port = config.port('zookeeper')
+    rest_port = config.port('zookeeper-rest')  # TODO: add zookeeper REST
+    client.send_port(port, rest_port)
+
+
+#
+# Rolling restart -- helpers and handlers
+#
+# When we add or remove a Zookeeper peer, Zookeeper needs to perform a
+# rolling restart of all of its peers, restarting the Zookeeper
+# "leader" last.
+#
+# The following functions accomplish this. Here's how they all fit together:
+#
+# (As you read, keep in mind that one node functions as the "leader"
+# in the context of Juju, and one node functions as the "leader" in
+# the context of Zookeeper; these nodes may or may not be the same.)
+#
+# 0. Whenever the Zookeeper server starts, it attempts to determine
+#    whether it is the Zookeeper leader. If so, it sets a flag on the
+#    Juju peer relation data.
+#
+# 1. When a node is added or remove from the cluster, the Juju leader
+#    runs `check_cluster`, and generates a "restart queue" comprising
+#    nodes in the cluster, with the Zookeeper lead node sorted last in
+#    the queue. It also sets a nonce, to identify this restart queue
+#    uniquely, and thus handle the situation where another node is
+#    added or restarted while we're still reacting to the first node's
+#    addition or removal. The leader drops the queue and nonce into
+#    the leadership data as "restart_queue" and "restart_nonce",
+#    respectively.
+#
+# 2. When any node detects a leadership.changed.restart_queue event,
+#    it runs `restart_for_quorum`, which is a noop unless the node's
+#    private address is the first element of the restart queue. In
+#    that case, if the node is the Juju leader, it will restart, then
+#    remove itself from the restart queue, triggering another
+#    leadership.changed.restart_queue event. If the node isn't the
+#    Juju leader, it will restart itself, then run `inform_restart`.
+#
+# 3. `inform_restart` will create a relation data changed event, which
+#    triggers `update_restart_queue` to run on the leader. This method
+#    will update the restart_queue, clearing any nodes that have
+#    restarted for the current nonce, and looping us back to step 2.
+#
+# 4. Once all the nodes have restarted, we should be in the following state:
+#
+#    * All nodes have an updated Zookeeper server running with the new
+#    * peer data.
+#
+#    * The Zookeeper leader has restarted last, which should help
+#      prevent orphaned jobs, per the Zookeeper docs.
+#
+#    * peers still have zkpeer.restarted.<nonce> set on their relation
+#      data. This is okay, as we will generate a new nonce next time,
+#      and the data is small.
+#
+# Edge cases and potential bugs:
+#
+# 1. Juju leader changes in the middle of a restart: this gets a
+#    little bit dicey, but it should work. The new leader should run
+#    `check_cluster_departed`, and start a new restart_queue.
+#
+
+def _ip_list(nodes):
+    '''
+    Given a list of nodes, in the format that our peer relation or
+    zookeeper lib will typically return node lists in, make a list of
+    just the ips (stripping ports, if they have been added).
+
+    We expect the list we passed in to look something like this:
+
+        [('zookeeper/0', '10.0.0.4'), ('zookeeper/1', '10.0.0.5')]
+
+    or this:
+
+        [('0', '10.0.0.4:2888:4888'), ('1', '10.0.0.5:2888:4888')]
+
+    We will return a list in the form:
+
+        ['10.0.0.4', '10.0.0.5']
+
+    '''
+    return [node[1].split(':')[0] for node in nodes]
+
+
+@when('zookeeper.started', 'leadership.is_leader', 'zkpeer.joined')
+@when_not('zkpeer.departed')
+def check_cluster(zkpeer):
+    '''
+    Checkup on the state of the cluster. Start a rolling restart if
+    the peers have changed.
+
+    '''
+    zk = Zookeeper()
+    if data_changed('zkpeer.nodes', zk.read_peers()):
+        peers = _ip_list(zk.sort_peers(zkpeer))
+        nonce = time.time()
+        hookenv.log('Quorum changed. Restart queue: {}'.format(peers))
+        leader_set(
+            restart_queue=json.dumps(peers),
+            restart_nonce=json.dumps(nonce)
+        )
+
+
+@when('zookeeper.started', 'leadership.is_leader', 'zkpeer.joined',
+      'zkpeer.departed')
+def check_cluster_departed(zkpeer, zkpeer_departed):
+    '''
+    Wrapper around check_cluster.
+
+    Together with check_cluster, implements the following logic:
+
+    "Run this when zkpeer.joined and zkpeer departed, or zkpeer.joined
+    and not zkpeer.departed"
+
+    '''
+    check_cluster(zkpeer)
+
+
+@when('leadership.changed.restart_queue', 'zkpeer.joined')
+def restart_for_quorum(zkpeer):
+    '''
+    If we're the next node in the restart queue, restart, and then
+    inform the leader that we've restarted. (If we are the leader,
+    remove ourselves from the queue, and update the leadership data.)
+
+    '''
+    private_address = hookenv.unit_get('private-address')
+    queue = json.loads(leader_get('restart_queue') or '[]')
+
+    if not queue:
+        # Everything has restarted.
+        return
+
+    if private_address == queue[0]:
+        # It's our turn to restart.
+        _restart_zookeeper('rolling restart for quorum update')
+        if is_state('leadership.is_leader'):
+            queue = queue[1:]
+            hookenv.log('Leader updating restart queue: {}'.format(queue))
+            leader_set(restart_queue=json.dumps(queue))
+        else:
+            zkpeer.inform_restart()
+
+
+@when('leadership.is_leader', 'zkpeer.joined')
+def update_restart_queue(zkpeer):
+    '''
+    If a Zookeeper node has restarted as part of a rolling restart,
+    pop it off of the queue.
+
+    '''
+    queue = json.loads(leader_get('restart_queue') or '[]')
+    if not queue:
+        return
+
+    restarted_nodes = _ip_list(zkpeer.restarted_nodes())
+    new_queue = [node for node in queue if node not in restarted_nodes]
+
+    if new_queue != queue:
+        hookenv.log('Leader updating restart queue: {}'.format(queue))
+        leader_set(restart_queue=json.dumps(new_queue))

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy
new file mode 100755
index 0000000..744a71f
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/01-deploy
@@ -0,0 +1,74 @@
+#!/usr/bin/python3
+
+# 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 unittest
+import amulet
+
+TIMEOUT = 1800
+
+
+class TestDeploy(unittest.TestCase):
+    """
+    Deployment test for Apache Zookkepper quorum
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.d = amulet.Deployment(series='xenial')
+
+        cls.d.add('zookeeper', charm='zookeeper', units=3)
+
+        cls.d.setup(timeout=TIMEOUT)
+        cls.d.sentry.wait(timeout=TIMEOUT)
+        cls.unit = cls.d.sentry['zookeeper'][0]
+
+    def test_deploy(self):
+        output, retcode = self.unit.run("pgrep -a java")
+        assert 'QuorumPeerMain' in output, "zookeeper QuorumPeerMain daemon is 
not started"
+
+    def test_quorum(self):
+        '''
+        Verify that our peers are talking to each other, and taking on
+        appropriate roles.
+
+        '''
+        self.assertEqual(3, len(self.d.sentry['zookeeper']))
+
+        # Verify that everything worked.
+        for unit in self.d.sentry['zookeeper']:
+            output, _ = unit.run(
+                "/usr/lib/zookeeper/bin/zkServer.sh status"
+            )
+            # Unit should be a leader or follower
+            self.assertTrue("leader" in output or "follower" in output)
+
+    def test_smoke(self):
+        """Validates Zookeeper using the Bigtop 'zookeeper' smoke test."""
+        smk_uuids = []
+
+        for unit in self.d.sentry['zookeeper']:
+            smk_uuids.append(unit.action_do("smoke-test"))
+
+        for smk_uuid in smk_uuids:
+            result = self.d.action_fetch(smk_uuid, full_output=True)
+            # zookeeper smoke-test sets outcome=success on success
+            if (result['outcome'] != "success"):
+                error = "Zookeeper smoke-test failed"
+                amulet.raise_status(amulet.FAIL, msg=error)
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address
new file mode 100755
index 0000000..66e42eb
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/10-bind-address
@@ -0,0 +1,115 @@
+#!/usr/bin/python3
+
+# 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 unittest
+import re
+import amulet
+import subprocess
+
+DEPLOY_TIMEOUT = 1800
+CONFIG_TIMEOUT = 900
+
+
+class TestBindClientPort(unittest.TestCase):
+    """
+    Test to verify that we can bind to listen for client connections
+    on a specific interface.
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        cls.d = amulet.Deployment(series='xenial')
+
+        cls.d.add('zookeeper', charm='zookeeper', units=3)
+
+        cls.d.setup(timeout=CONFIG_TIMEOUT)
+        cls.d.sentry.wait(timeout=DEPLOY_TIMEOUT)
+        cls.unit = cls.d.sentry['zookeeper'][0]
+
+    def test_bind_port(self):
+        """
+        Test to verify that we update client port bindings successfully.
+
+        """
+        network_interface = None
+        # Regular expression should handle interfaces in the format
+        # eth[n], and in the format en[foo] (the "predicatble
+        # interface names" in v197+ of systemd).
+        ethernet_interface = re.compile('^e[thn]+.*')
+        interfaces, _ = self.unit.run(
+            "ifconfig -a | sed 's/[ \t].*//;/^$/d'")
+        interfaces = interfaces.split()  # Splits on newlines
+        for interface in interfaces:
+            if ethernet_interface.match(interface):
+                network_interface = interface
+                break
+
+        if network_interface is None:
+            raise Exception(
+                "Could not find any interface on the unit that matched my "
+                "criteria.")
+        # self.d.configure broken due to change in juju api. TODO:
+        # switch this out when fixed.
+        subprocess.check_call(
+            ['juju', 'config', 'zookeeper', 'network_interface={}'.format(
+                network_interface)])
+        #self.d.configure('zookeeper', {'network_interface': 
network_interface})
+        self.d.sentry.wait_for_messages(
+            {'zookeeper': 'updating network interface'}, 
timeout=CONFIG_TIMEOUT)
+        self.d.sentry.wait_for_messages(
+            {'zookeeper': 'ready (3 zk nodes)'}, timeout=CONFIG_TIMEOUT)
+        ret = self.unit.run(
+            'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0]
+        matcher = re.compile(
+            "^clientPortAddress=\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*")
+
+        self.assertTrue(matcher.match(ret))
+
+        # Verify that smoke tests still run
+        smk_uuid = self.unit.action_do("smoke-test")
+        result = self.d.action_fetch(smk_uuid, full_output=True)
+        # zookeeper smoke-test sets outcome=success on success
+        if (result['outcome'] != "success"):
+            error = "Zookeeper smoke-test failed"
+            amulet.raise_status(amulet.FAIL, msg=error)
+
+    @unittest.skip(
+        'Broken handling of 0.0.0.0 bindings upstream, in Zookeeper project.')
+    def test_reset_bindings(self):
+        """
+        Verify that we can reset the client port bindings to 0.0.0.0
+
+        """
+        self.d.configure('zookeeper', {'network_interface': '0.0.0.0'})
+        self.d.sentry.wait_for_messages(
+            {'zookeeper': 'updating network interface'}, 
timeout=CONFIG_TIMEOUT)
+        self.d.sentry.wait_for_messages(
+            {'zookeeper': 'ready (3 zk nodes)'}, timeout=CONFIG_TIMEOUT)
+        ret = self.unit.run(
+            'grep clientPortAddress /etc/zookeeper/conf/zoo.cfg')[0]
+
+        matcher = re.compile("^clientPortAddress=0\.0\.0\.0.*")
+        self.assertTrue(matcher.match(ret))
+
+        # Verify that smoke tests still run
+        smk_uuid = self.unit.action_do("smoke-test")
+        output = self.d.action_fetch(smk_uuid, full_output=True)
+        assert "completed" in output['status']
+
+
+if __name__ == '__main__':
+    unittest.main()

http://git-wip-us.apache.org/repos/asf/bigtop/blob/89d3ac48/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/tests.yaml
----------------------------------------------------------------------
diff --git 
a/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/tests.yaml 
b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/tests.yaml
new file mode 100644
index 0000000..3b6ce3e
--- /dev/null
+++ b/bigtop-packages/src/charm/zookeeper/layer-zookeeper/tests/tests.yaml
@@ -0,0 +1,3 @@
+reset: false
+packages:
+  - amulet

Reply via email to