~ibmcharmers/xenial/ibm-was-base-5

Owner: geethas1
Status: Needs Fixing
Vote: -1 (+2 needed for approval)

CPP?: No
OIL?: No

Hello Team,

Here is a charm for IBM WebSphere Application Server Base(IBM WAS Base V9.0.0) on Z platform for review.

To test this charm, you should accept the license/terms for IBM Installation Manager, IBM WAS Base and IBM Java SDK.

Layered charm can be found in the below repository.

Repo : https://code.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-was-base/trunk

ibm-was-base charm has been pushed into charm store:

cs:~ibmcharmers/xenial/ibm-was-base-5

link: https://jujucharms.com/u/ibmcharmers/ibm-was-base/xenial/5

Thanks


Tests

Substrate Status Results Last Updated
aws RETRY 19 days ago
gce RETRY 19 days ago
lxc RETRY 19 days ago

Voted: -1
petevg wrote 3 months ago
Hi,

Thank you for your continued work on the charm.

I ran into two issues while installing:

1) The README suggests running "juju set-constraints". Since we don't have a charm deployed at this time, it should suggest running "juju set-model-contraints" instead.
2) It is unclear how the files listed under the prerequisites map to the resources suggested in the command line invocation. It would be nice if the names in the command line invocation matched the names in the list of suggested resources.

There are still a few holdovers from my last comment, too:

1) It looks like the charm is still logging as password on line 61 of ibm-was-base.sh.
2) The admonishment to remove and re-add relations on config changes is still in the README. It sounds like this is a workaround for an issue in the IHS charm, where it may not be taking advantage of the "was-base.changed" event in the ihs interface to react to config changes. A fix in the IHS charm would look something like the following:

@when "ibm-was-base.changed"
function update_config() {
# Get the updated data via "relation-get"
# Execute the same code that you have in your .joined handler
}

Thank you again for your work on this charm! Let me know if you have any questions about the comments above.

Add Comment

Login to comment/vote on this review.


Policy Checklist

Description Unreviewed Pass Fail

General

Must verify that any software installed or utilized is verified as coming from the intended source.
  • Any software installed from the Ubuntu or CentOS default archives satisfies this due to the apt and yum sources including cryptographic signing information.
  • Third party repositories must be listed as a configuration option that can be overridden by the user and not hard coded in the charm itself.
  • Launchpad PPAs are acceptable as the add-apt-repository command retrieves the keys securely.
  • Other third party repositories are acceptable if the signing key is embedded in the charm.
Must provide a means to protect users from known security vulnerabilities in a way consistent with best practices as defined by either operating system policies or upstream documentation. petevg
Basically, this means there must be instructions on how to apply updates if you use software not from distribution channels.
Must have hooks that are idempotent. petevg
Should be built using charm layers. petevg
Should use Juju Resources to deliver required payloads. petevg

Testing and Quality

charm proof must pass without errors or warnings. petevg
Must include passing unit, functional, or integration tests.
Tests must exercise all relations.
Tests must exercise config.
set-config, unset-config, and re-set must be tested as a minimum
Must not use anything infrastructure-provider specific (i.e. querying EC2 metadata service). petevg
Must be self contained unless the charm is a proxy for an existing cloud service, e.g. ec2-elb charm.
Must not use symlinks. petevg
Bundles must only use promulgated charms, they cannot reference charms in personal namespaces. petevg
Must call Juju hook tools (relation-*, unit-*, config-*, etc) without a hard coded path. petevg
Should include a tests.yaml for all integration tests. petevg

Metadata

Must include a full description of what the software does. petevg
Must include a maintainer email address for a team or individual who will be responsive to contact. petevg
Must include a license. Call the file 'copyright' and make sure all files' licenses are specified clearly. petevg
Must be under a Free license. petevg
Must have a well documented and valid README.md. petevg
Must describe the service. petevg
Must describe how it interacts with other services, if applicable. petevg
Must document the interfaces. petevg
Must show how to deploy the charm. petevg
Must define external dependencies, if applicable. petevg
Should link to a recommend production usage bundle and recommended configuration if this differs from the default.
Should reference and link to upstream documentation and best practices.

Security

Must not run any network services using default passwords. petevg
Must verify and validate any external payload petevg
  • Known and understood packaging systems that verify packages like apt, pip, and yum are ok.
  • wget | sh style is not ok.
Should make use of whatever Mandatory Access Control system is provided by the distribution. petevg
Should avoid running services as root. petevg

All changes | Changes since last revision

Source Diff

Files changed 69

Inline diff comments 0

No comments yet.

Back to file index

Makefile

 1
--- 
 2
+++ Makefile
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/make
 5
+
 6
+all: lint unit_test
 7
+
 8
+
 9
+.PHONY: clean
10
+clean:
11
+	@rm -rf .tox
12
+
13
+.PHONY: apt_prereqs
14
+apt_prereqs:
15
+	@# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip)
16
+	@which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox)
17
+
18
+.PHONY: lint
19
+lint: apt_prereqs
20
+	@tox --notest
21
+	@PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests)
22
+	@charm proof
23
+
24
+.PHONY: unit_test
25
+unit_test: apt_prereqs
26
+	@echo Starting tests...
27
+	tox
Back to file index

README.md

  1
--- 
  2
+++ README.md
  3
@@ -0,0 +1,180 @@
  4
+Charm for IBM WebSphere Application Server Base (WAS Base) 9.0.0
  5
+
  6
+## Overview
  7
+
  8
+WebSphere Application Server is a proven, high-performance transaction engine that can help build, run, integrate, and manage dynamic web applications.
  9
+For details on IBM WebSphere Application Server Base, as well as information on purchasing, please visit: [Product Page] [was-product-page] and at the [Passport Advantage Site] [Passport]. More information available at the [IBM Knowledge Center] [WAS-Infocenter].
 10
+
 11
+`IBM WebSphere Application Server Base`
 12
+
 13
+This charm is using IBM Installation Manager to install IBM WebSphere Application Server Base Software. To install Installation Manager tool, ibm-im charm is used as a base layer for this charm.
 14
+
 15
+`IBM SDK, Java Technology Edition`
 16
+
 17
+WebSphere Application Server now uses a separately packaged, IBM Java SDK. It requires Java SDK version 8.
 18
+
 19
+## Prerequisites
 20
+
 21
+This charm makes use of resources, a feature only available in Juju 2.0. During deploy or upgrade, you will need to specify the installable package(s)
 22
+required by this charm. Download your licensed IBM WebSphere Application Server Base packages and IBM SDK, Java Technology Edition from the [Product Page][WAS-9.0.0-download].
 23
+
 24
+### Packages for Ubuntu on Z (s390x):
 25
+
 26
+IBM Installation Manager 1.8 (`agent.installer.linux.gtk.s390x_1.8.5000.20160506_1125.zip`)(CND0XML)
 27
+
 28
+IBM WAS Base V9.0 (`was.repo.9000.base.zip`) (CND1AML)
 29
+
 30
+IBM SDK, Java Technology Edition, V8 (`sdk.repo.8030.java8.linux.zip`) (CND18ML)
 31
+
 32
+
 33
+## Usage
 34
+
 35
+To use this charm, you must agree to the Terms of Use. You can view the full license for `IBM Installation Manager` [im-license-info](http://www-03.ibm.com/software/sla/sladb.nsf/displaylis/39AFC1C1D485C4E085257E7300548B05?OpenDocument) , 
 36
+ `IBM WebSphere Application Server Base` [was-license-info] [was-license] and `IBM SDK, Java Technology Edition` [java-sdk-license-info] [java-sdk-license] products.
 37
+
 38
+
 39
+## Memory and Disk Requirements
 40
+
 41
+Minimum 2.0 GB of disk space for installed image and Minimum 1 GB of physical memory recommended. 
 42
+
 43
+WebSphere Application Server Charm requires 15 GB of root disk to download packages and install the IBM WebSphere Application Server Base Software. By default available root disk space is 8GB.
 44
+
 45
+To request a larger root disk, run the following command:
 46
+
 47
+	juju set-constraints root-disk=15G
 48
+
 49
+### Deploy
 50
+
 51
+1. Run the following commands to deploy this charm:
 52
+
 53
+
 54
+	juju deploy ibm-was-base --resource ibm_im_installer=</path/to/ibm_im_installer.zip> --resource ibm_was_base_installer=</path/to/ibm_was_base_installer.zip> --resource ibm_java_sdk_installer=</path/to/ibm_java_sdk_installer.zip>
 55
+
 56
+
 57
+**Note**: This charm requires acceptance of Terms of Use. When deploying from the Charm Store, these terms will be presented to you for your consideration.
 58
+To accept the terms:
 59
+
 60
+	juju agree ibm-im/2 ibm-was-base/3 ibm-java-sdk/1
 61
+	juju deploy ibm-was-base
 62
+
 63
+
 64
+2. To create application server profile, `manageprofiles` command needs values for profile name, profile path, user name and password. To provide these run the following command:
 65
+
 66
+	juju config ibm-was-base profile_name=<profile_name>
 67
+	juju config ibm-was-base profile_path=<profile_path>
 68
+	juju config ibm-was-base was_admin_user=<user_name>
 69
+	juju config ibm-was-base was_admin_pw=<password>
 70
+
 71
+	For eg:
 72
+
 73
+	juju config ibm-was-base profile_name="profile01"
 74
+	juju config ibm-was-base profile_path="/opt/profiles"
 75
+	juju config ibm-was-base was_admin_user="admin1"
 76
+	juju config ibm-was-base was_admin_pw="admin123"
 77
+
 78
+
 79
+**Note**: If relation established between WAS Base and IHS, Please remove the relation before changing these config options.
 80
+
 81
+If any of these values is not set, then the charm will use default values for profile creation. Once WAS Base charm is deployed successfully, user can also delete existing profile and create new application server profile by setting any of these configuration options.
 82
+
 83
+
 84
+3. To Expose the IBM WAS Base Service to public, run the following command:
 85
+
 86
+	juju expose ibm-was-base
 87
+
 88
+
 89
+### Upgrade
 90
+
 91
+Once deployed, user can upgrade the existing installation by installing fixpacks:
 92
+
 93
+If user wants to upgrade existing installation of Installtion Manger, run the following command:
 94
+
 95
+	juju attach ibm-was-base ibm_im_fixpack=</path/to/fixpack.zip>
 96
+
 97
+To upgrade WAS Base installation:
 98
+
 99
+	juju attach ibm-was-base ibm_was_base_fp=</path/to/ibm_was_base_fp.zip>
100
+	juju attach ibm-was-base ibm_java_sdk_fp=</path/to/ibm_java_sdk_fp.zip>
101
+
102
+
103
+### Verification
104
+
105
+After installing IBM WebSphere Application Server Base, use your web browser to see the WebSphere Application Server admin console. The URLs for WAS Base admin console are:
106
+
107
+	http://was-base-host:9060/ibm/console 
108
+	https://was-base-host:9043/ibm/console
109
+	
110
+Here was-base-host represents the public ip address of your machine, where WAS Base is installed. 
111
+
112
+To login WAS Base admin console use user name and password created in step 2 in `Deploy` section.
113
+
114
+
115
+## Configuration
116
+
117
+`profile_name`
118
+
119
+The profile name is used to create application server profile.
120
+
121
+`profile_path`
122
+
123
+The location of the profile to be created.
124
+
125
+`was_admin_user`
126
+
127
+Admin user name to log in WAS admin console
128
+
129
+`was_admin_pw`
130
+
131
+The password used to log in WAS admin console.
132
+
133
+		
134
+## IBM Installation Manager(IM) Information
135
+
136
+(1) General Information
137
+Details about IM available at [IBM Knowledge Center][Im-info].
138
+
139
+(2) Download Information
140
+Information on procuring IM product is available at the [Product Page][im-product-page]
141
+and at the [Passport Advantage Site][passport].	
142
+
143
+## IBM WebSphere Application Server Base Information
144
+
145
+(1) General Information 
146
+
147
+Details about IBM WAS Base software available at [IBM Knowledge Center] [WAS-Infocenter].
148
+
149
+Information on procuring WAS Base product is available at the [Passport Advantage site] [Passport]
150
+
151
+(2) Download Information
152
+
153
+Details about WAS 9.0.0 download available [here] [[WAS-9.0.0-download]
154
+
155
+Details about IBM Java SDK version 8 download available [here] [java-sdk-download]
156
+
157
+More information about IBM IM Downloadable file and WAS Base Downloadable files available [here] [IBM-IM-more-info]
158
+
159
+## Contact Information
160
+
161
+For issues with this charm, please contact IBM Juju Support team <jujusupp@us.ibm.com>
162
+
163
+<!-- Links -->
164
+
165
+[IM-info]: http://www-01.ibm.com/support/knowledgecenter/SSDV2W/im_family_welcome.html
166
+
167
+[im-product-page]: http://www-01.ibm.com/support/docview.wss?uid=swg27025142
168
+
169
+[WAS-Infocenter]: http://www.ibm.com/support/knowledgecenter/SSEQTP_9.0.0/com.ibm.websphere.base.doc/ae/welcome_base.html
170
+
171
+[was-product-page]: http://www-03.ibm.com/software/products/en/appserv-was
172
+
173
+[WAS-9.0.0-download]: http://www-01.ibm.com/support/docview.wss?uid=swg27048319
174
+
175
+[java-sdk--download]: http://www-01.ibm.com/support/docview.wss?uid=swg27048319#jdk
176
+
177
+[Passport]: http://www-01.ibm.com/software/passportadvantage/
178
+
179
+[IBM-IM-more-info]: http://www-01.ibm.com/support/docview.wss?uid=swg24038380
180
+
181
+[was-license]: http://www-03.ibm.com/software/sla/sladb.nsf/lilookup/E0B1C3C1F5B537F285257FDF00359288?OpenDocument
182
+
183
+[java-sdk-license]: http://www-03.ibm.com/software/sla/sladb.nsf/lilookup/292DB2B39A911D6485257FD10065CAAA?OpenDocument
Back to file index

bin/layer_option

 1
--- 
 2
+++ bin/layer_option
 3
@@ -0,0 +1,24 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import sys
 7
+sys.path.append('lib')
 8
+
 9
+import argparse
10
+from charms.layer import options
11
+
12
+
13
+parser = argparse.ArgumentParser(description='Access layer options.')
14
+parser.add_argument('section',
15
+                    help='the section, or layer, the option is from')
16
+parser.add_argument('option',
17
+                    help='the option to access')
18
+
19
+args = parser.parse_args()
20
+value = options(args.section).get(args.option, '')
21
+if isinstance(value, bool):
22
+    sys.exit(0 if value else 1)
23
+elif isinstance(value, list):
24
+    for val in value:
25
+        print(val)
26
+else:
27
+    print(value)
Back to file index

config.yaml

 1
--- 
 2
+++ config.yaml
 3
@@ -0,0 +1,22 @@
 4
+"options":
 5
+  "profile_name":
 6
+    "type": "string"
 7
+    "default": "AppSrv01"
 8
+    "description": |
 9
+      The profile name is used to create application server profile.
10
+  "profile_path":
11
+    "type": "string"
12
+    "default": "/root/IBM/WebSphere/AppServer/V85/BASE/profiles"
13
+    "description": |
14
+      The location of the profile to be created.
15
+  "was_admin_user":
16
+    "type": "string"
17
+    "default": "wsadmin"
18
+    "description": |
19
+      Admin user name to log in WAS admin console.
20
+  "was_admin_pw":
21
+    "type": "string"
22
+    "default": "wsadmin"
23
+    "description": |
24
+      The password used to log in WAS admin console.
25
+
Back to file index

copyright

 1
--- 
 2
+++ copyright
 3
@@ -0,0 +1,13 @@
 4
+Copyright 2015 IBM Corporation
 5
+
 6
+This Charm is licensed under the Apache License, Version 2.0 (the "License");
 7
+you may not use this file except in compliance with the License.
 8
+You may obtain a copy of the License at
 9
+
10
+    http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+Unless required by applicable law or agreed to in writing, software
13
+distributed under the License is distributed on an "AS IS" BASIS,
14
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+See the License for the specific language governing permissions and
16
+limitations under the License.
Back to file index

hooks/config-changed

 1
--- 
 2
+++ hooks/config-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/hook.template

 1
--- 
 2
+++ hooks/hook.template
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/install

 1
--- 
 2
+++ hooks/install
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-elected

 1
--- 
 2
+++ hooks/leader-elected
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/leader-settings-changed

 1
--- 
 2
+++ hooks/leader-settings-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/relations/db2/README.md

 1
--- 
 2
+++ hooks/relations/db2/README.md
 3
@@ -0,0 +1,66 @@
 4
+# Overview
 5
+
 6
+This interface layer handles the communication between IBM DB2 and Consumer
 7
+charms. The provider end of this interface provides the IBM DB2 service. The
 8
+consumer part requires the existence of a provider to function.
 9
+
10
+## Usage
11
+
12
+### Provides
13
+
14
+This interface layer will set the following states, as appropriate:
15
+
16
+- `{relation_name}.connected`: The relation is established, but IBM DB2 may not
17
+yet have provided any connection or service information.
18
+
19
+- `{relation_name}.sshconfigured`: A consumer ssh key has been added to the
20
+IBM DB2 `authorized_keys` file.
21
+
22
+- `{relation_name}.ready`: IBM DB2 has established relation with the consumer
23
+charm and the SSH key has been exchanged. DB2 has provided its connection
24
+string information, and is ready to accept requests from the consumer.
25
+
26
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
27
+related to the consumer charm should happen now on the IBM DB2 charm since the
28
+consumer is going away.
29
+
30
+
31
+### Requires
32
+
33
+Consumers like WAS, Mobile First Server, etc require this interface to connect
34
+to IBM DB2. This interface layer will set the following states, as appropriate:
35
+
36
+- `{relation_name}.connected`: The consumer charm has been related to IBM DB2.
37
+At this point, the consumer charm sends the SSH key to IBM DB2 charm.
38
+
39
+    For example, the consumer charm can send the ssh key to IBM DB2 charm as follows:
40
+
41
+    ```
42
+    @when 'db.connected'
43
+    @when_not 'db.sshconfigured'
44
+    function configure_sshkeys_dbs() {
45
+        SSH_PATH=/root/.ssh
46
+        if [ ! -f  $SSH_PATH/id_rsa.pub ]; then
47
+                juju-log "Setting up SSH keys."
48
+                ssh-keygen -t rsa -f $SSH_PATH/id_rsa -N ''
49
+        fi
50
+        key="`cat $SSH_PATH/id_rsa.pub`"
51
+        relation_call --state=db.connected set_ssh_keys "$key" || true
52
+    }
53
+    ```
54
+
55
+-  `{relation_name}.ready`: IBM DB2 is ready to send connection information.
56
+The consumer charm can access the IBM DB2 details from the interface using the
57
+following methods:
58
+
59
+   - get_db2_hostname() - host name of IBM DB2
60
+   - get_db2_port() - port number of IBM DB2
61
+   - get_db2_path() - the install location of IBM DB2
62
+   - get_dbusername() - User name created for the consumer service to connect to DB2
63
+   - get_dbuserpw() - Password for connecting to DB2
64
+   - get_db2_instance_name() - Instance name created for the consumer service
65
+
66
+
67
+- `{relation_name}.departed`: The relation has been removed. Any cleanup
68
+related to IBM DB2 should happen now on the consumer charm since IBM DB2 is
69
+going away.
Back to file index

hooks/relations/db2/interface.yaml

1
--- 
2
+++ hooks/relations/db2/interface.yaml
3
@@ -0,0 +1,4 @@
4
+name: db2
5
+summary: Facilitates communication between IBM DB2 and database consumers
6
+version: 1
7
+maintainer: "IBM Juju Support Team <jujusupp@us.ibm.com>"
Back to file index

hooks/relations/db2/provides.py

 1
--- 
 2
+++ hooks/relations/db2/provides.py
 3
@@ -0,0 +1,80 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class db2Provides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.SERVICE
12
+    # convenient way to provide accessor methods
13
+    auto_accessors = ['db2_path', 'db2_port']
14
+
15
+    @hook('{provides:db2}-relation-joined')
16
+    def joined(self):
17
+        conversation = self.conversation()
18
+        conversation.remove_state('{relation_name}.departed')
19
+        conversation.set_state('{relation_name}.connected')
20
+
21
+    @hook('{provides:db2}-relation-changed')
22
+    def changed(self):
23
+        conversation = self.conversation()
24
+        conversation.remove_state('{relation_name}.departed')
25
+        conversation.set_state('{relation_name}.connected')
26
+        if str(conversation.get_remote('ssh_key')) != "None":
27
+            conversation.set_state('{relation_name}.ready')
28
+
29
+    @hook('{provides:db2}-relation-departed')
30
+    def departed(self):
31
+        conversation = self.conversation()
32
+        conversation.remove_state('{relation_name}.connected')
33
+        conversation.remove_state('{relation_name}.ready')
34
+        conversation.set_state('{relation_name}.departed')
35
+
36
+    def dismiss(self, service):
37
+        conversation = self.conversation(service)
38
+        conversation.remove_state('{relation_name}.departed')
39
+
40
+    def reset_states(self, service):
41
+        conversation = self.conversation(service)
42
+        conversation.remove_state('{relation_name}.connected')
43
+        conversation.remove_state('{relation_name}.departed')
44
+        conversation.remove_state('{relation_name}.ready')
45
+
46
+    def set_db_details(self, service, db2_path, db2_port, hostname,
47
+                       dbusername, dbuserpw, db2_instance_name, dbs_created):
48
+        conversation = self.conversation(service)
49
+        conversation.set_remote(data={
50
+            'db2_ready': True,
51
+            'db2_path': db2_path,
52
+            'db2_port': db2_port,
53
+            'hostname': hostname,
54
+            'dbusername': dbusername,
55
+            'dbuserpw': dbuserpw,
56
+            'db2_instance_name': db2_instance_name,
57
+            'dbs_created': dbs_created,
58
+        })
59
+
60
+    def set_sshconfigured(self, service):
61
+        conversation = self.conversation(service)
62
+        conversation.set_state('{relation_name}.sshconfigured')
63
+
64
+    def dismiss_sshconfigured(self, service):
65
+        conversation = self.conversation(service)
66
+        conversation.remove_state('{relation_name}.sshconfigured')
67
+
68
+    def get_sshkeys(self, service):
69
+        conversation = self.conversation(service)
70
+        return conversation.get_remote('ssh_key')
71
+
72
+    def get_dbnames(self, service):
73
+        conversation = self.conversation(service)
74
+        return conversation.get_remote('db_names')
75
+
76
+    def services(self):
77
+        """
78
+        Return a list of services requesting databases.
79
+        """
80
+        service = []
81
+        for conversation in self.conversations():
82
+            service.append(conversation.scope)
83
+        return service
Back to file index

hooks/relations/db2/requires.py

 1
--- 
 2
+++ hooks/relations/db2/requires.py
 3
@@ -0,0 +1,65 @@
 4
+# Licensed under the Apache License, Version 2.0 (the "License");
 5
+# you may not use this file except in compliance with the License.
 6
+# You may obtain a copy of the License at
 7
+#
 8
+#     http://www.apache.org/licenses/LICENSE-2.0
 9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+from charms.reactive import hook
17
+from charms.reactive import RelationBase
18
+from charms.reactive import scopes
19
+
20
+
21
+class db2Requires(RelationBase):
22
+    scope = scopes.GLOBAL
23
+    auto_accessors = ['ssh_key']
24
+
25
+    @hook('{requires:db2}-relation-joined')
26
+    def joined(self):
27
+        self.remove_state('{relation_name}.departed')
28
+        self.set_state('{relation_name}.connected')
29
+
30
+    @hook('{requires:db2}-relation-changed')
31
+    def changed(self):
32
+        self.remove_state('{relation_name}.departed')
33
+        self.set_state('{relation_name}.connected')
34
+        if str(self.get_remote('db2_port')) != "None":
35
+            self.set_state('{relation_name}.ready')
36
+
37
+    @hook('{requires:db2}-relation-departed')
38
+    def departed(self):
39
+        self.remove_state('{relation_name}.connected')
40
+        self.remove_state('{relation_name}.ready')
41
+        self.set_state('{relation_name}.departed')
42
+
43
+    def get_db2_port(self):
44
+        return self.get_remote('db2_port')
45
+
46
+    def get_db2_path(self):
47
+        return self.get_remote('db2_path')
48
+
49
+    def get_db2_hostname(self):
50
+        return self.get_remote('hostname')
51
+
52
+    def get_dbusername(self):
53
+        return self.get_remote('dbusername')
54
+
55
+    def get_dbuserpw(self):
56
+        return self.get_remote('dbuserpw')
57
+
58
+    def get_db2_instance_name(self):
59
+        return self.get_remote('db2_instance_name')
60
+
61
+    def get_db2_dbnames(self):
62
+        return self.get_remote('dbs_created')
63
+
64
+    def set_ssh_keys(self, ssh_key):
65
+        self.set_remote('ssh_key', ssh_key)
66
+
67
+    def set_dbs(self, db_names):
68
+        self.set_remote('db_names', db_names)
Back to file index

hooks/relations/ibm-mq/README.md

 1
--- 
 2
+++ hooks/relations/ibm-mq/README.md
 3
@@ -0,0 +1,42 @@
 4
+Overview
 5
+-----------
 6
+
 7
+This interface layer handles the communication between `IBM MQ` and other charms that tries to communicate by sending each other data in messages through IBM-MQ rather than by calling each other directly.
 8
+The provider end of this interface provides the Queue Manager name, Queue Name, IP Address and Port.
 9
+The consumer/requires part will be any other charm and can get all the above values viz. Queue Manager name, Queue Name, IP Address and Port to send or receive messages. Here we are describing consumer charm as `ibm-was-base`
10
+
11
+
12
+Usage
13
+------
14
+
15
+#### Provides 
16
+IBM MQ product will provide this interface. This interface layer will set the following states, as appropriate:
17
+
18
+ - `{relation_name}.connected`: The relation is established, IBM-MQ is ready to send it's information.
19
+ 
20
+	- `get_consumer_hostname()` - returns/gets the consumer hostname to create a channel authentication rule that allows client system to use the channel by entering the MQSC command.
21
+
22
+	- `set_mq_details()` - Sets Queue Manager name, Queue Name, IP Address and Port for the connected services. 
23
+						As a Queue Name it sets a filename where MQSC Commands can be edited for further operation. 
24
+						
25
+	
26
+ - `{relation_name}.departed` : The relation has been removed. Any cleanup related to the consumer charm (e.g IBM WebSphere Server) should happen now on the Consumer charm since the consumer is going away.
27
+
28
+#### Requires
29
+
30
+Consumer charms e.g `IBM WebSphere Application Server` will require this interface to connect to IBM-MQ so that they can get the necessary information about `IBM-MQ` to send/receive messages to other Applications. 
31
+This interface layer will set the following states, as appropriate:
32
+
33
+- `{relation_name}.connected` : The consumer charm has been related to a IBM-MQ provider charm. 
34
+	At this point, the charm waits for Provider charm to send details like Queue Manager name, Queue Name, IP Address and Port.
35
+	
36
+	- `set_hostname()` - sets the `hostname`, so that IBM-MQ can get the consumer/client hostname to allow to send/receive messages.
37
+
38
+- `{relation_name}.ready` : The consumer charm e.g `IBM WebSphere Application Server` is now ready to access the details of IBM-MQ to send/recieve messages. Such as Queue Manager name, Queue Name, IP Address/hostname and Port.
39
+
40
+    - `get_qm_name()` - returns the `QM_Name` that IBM-MQ has created.
41
+	- `get_qname()`  - returns the `QName` that IBM-MQ has created.
42
+	- `get_mq_hostname()` - returns the `hostname` that IBM-MQ has created.
43
+	- `get_mq_port()` - returns the `MQ Port` that IBM-MQ has created.
44
+
45
+- `{relation_name}.departed` : The relation has been removed. Any cleanup related to the provider charm should happen now. 
Back to file index

hooks/relations/ibm-mq/interface.yaml

1
--- 
2
+++ hooks/relations/ibm-mq/interface.yaml
3
@@ -0,0 +1,4 @@
4
+name: ibm-mq
5
+summary: Interface for relating to ibm-mq
6
+version: 1
7
+maintainer: "Juju Support <jujusupp@us.ibm.com>"
Back to file index

hooks/relations/ibm-mq/provides.py

 1
--- 
 2
+++ hooks/relations/ibm-mq/provides.py
 3
@@ -0,0 +1,53 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class mqProvides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.SERVICE
12
+
13
+    # convenient way to provide accessor methods
14
+    auto_accessors = ['QM_Name', 'QName', 'host', 'port']
15
+
16
+    @hook('{provides:ibm-mq}-relation-joined')
17
+    def joined(self):
18
+        conversation = self.conversation()
19
+        conversation.remove_state('{relation_name}.departed')
20
+        conversation.set_state('{relation_name}.connected')
21
+
22
+    @hook('{provides:ibm-mq}-relation-departed')
23
+    def departed(self):
24
+        conversation = self.conversation()
25
+        conversation.remove_state('{relation_name}.connected')
26
+        conversation.set_state('{relation_name}.departed')
27
+
28
+    def dismiss(self, service):
29
+        conversation = self.conversation(service)
30
+        conversation.remove_state('{relation_name}.departed')
31
+
32
+    def reset_states(self, service):
33
+        conversation = self.conversation(service)
34
+        conversation.remove_state('{relation_name}.connected')
35
+        conversation.remove_state('{relation_name}.departed')
36
+
37
+    def set_mq_details(self, service, QM_Name, Qname, host, port):
38
+        conversation = self.conversation(service)
39
+        conversation.set_remote(data={
40
+            'QM_Name': QM_Name,
41
+            'Qname': Qname,
42
+            'host': host,
43
+            'port': port,
44
+        })
45
+
46
+    def get_consumer_hostname(self):
47
+        return self.get_remote('host')
48
+
49
+    def services(self):
50
+        """
51
+        Return a list of services requesting MQ.
52
+        """
53
+        service = []
54
+        for conversation in self.conversations():
55
+            service.append(conversation.scope)
56
+        return service
Back to file index

hooks/relations/ibm-mq/requires.py

 1
--- 
 2
+++ hooks/relations/ibm-mq/requires.py
 3
@@ -0,0 +1,47 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class mq1Requires(RelationBase):
10
+    scope = scopes.GLOBAL
11
+
12
+#    auto_accessors = ['QM_Name','Qname','host','port']
13
+
14
+    @hook('{requires:ibm-mq}-relation-joined')
15
+    def joined(self):
16
+        self.remove_state('{relation_name}.departed')
17
+        self.set_state('{relation_name}.connected')
18
+
19
+    @hook('{requires:ibm-mq}-relation-changed')
20
+    def changed(self):
21
+        self.set_state('{relation_name}.available')
22
+        print("Status is relation_name.available in requires")
23
+        if str(self.get_remote('port')) != "None":
24
+            self.set_state('{relation_name}.ready')
25
+            print("Status is relation_name.ready in requires")
26
+
27
+    @hook('{requires:ibm-mq}-relation-departed')
28
+    def departed(self):
29
+        self.remove_state('{relation_name}.connected')
30
+        self.remove_state('{relation_name}.available')
31
+        self.remove_state('{relation_name}.ready')
32
+        self.set_state('{relation_name}.departed')
33
+
34
+    def set_hostname(self, host):
35
+        conversation = self.conversation()
36
+        conversation.set_remote(data={
37
+            'host': host,
38
+        })
39
+
40
+    def get_qm_name(self):
41
+        return self.get_remote('QM_Name')
42
+
43
+    def get_qname(self):
44
+        return self.get_remote('Qname')
45
+
46
+    def get_mq_hostname(self):
47
+        return self.get_remote('host')
48
+
49
+    def get_mq_port(self):
50
+        return self.get_remote('port')
Back to file index

hooks/relations/was-ihs/@

 1
--- 
 2
+++ hooks/relations/was-ihs/@
 3
@@ -0,0 +1,75 @@
 4
+# Licensed under the Apache License, Version 5.0 (the "License");
 5
+# you may not use this file except in compliance with the License.
 6
+# You may obtain a copy of the License at
 7
+#
 8
+#     http://www.apache.org/licenses/LICENSE-2.0
 9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+# See the License for the specific language governing permissions and
14
+# limitations under the License.
15
+
16
+from charms.reactive import hook
17
+from charms.reactive import RelationBase
18
+from charms.reactive import scopes
19
+
20
+
21
+class db2Requires(RelationBase):
22
+    scope = scopes.GLOBAL
23
+    
24
+    auto_accessors = ['ssh_key1', 'ssh_key2' , 'ssh_key32']
25
+ 
26
+    @hook('{requires:db2}-relation-joined')
27
+    def joined(self):
28
+        self.set_state('{relation_name}.connected')
29
+
30
+    @hook('{requires:db2}-relation-changed')
31
+    def changed(self):
32
+        self.set_state('{relation_name}.available')
33
+
34
+    @hook('{requires:db2}-relation-departed')
35
+    def departed(self):
36
+        self.remove_state('{relation_name}.connected')
37
+        self.remove_state('{relation_name}.available')
38
+
39
+
40
+    # For minor upgrades, provide a way to set java-version independently
41
+    def set_port(self, db2_port):
42
+        self.set_remote('db2_port', db2_port)
43
+
44
+    def unset_ready(self):
45
+        self.set_remote('db2_ready', False)
46
+    
47
+    def get_db_values(self):
48
+        """
49
+        Get the connection string, if available, or None.
50
+        """
51
+        data = {
52
+            'host': self.host(),
53
+            'port': db2-port,
54
+            'path': db2-path,
55
+        }
56
+        if all(data.values()):
57
+            return str.format(
58
+                'host={host} port={port} =path{path}',
59
+                **data)
60
+        return None 
61
+
62
+    def get_port(self):
63
+        #print("In get_port function ",db2_port)
64
+        return self.get_remote('db2_port')
65
+    
66
+#to pass the ssh keys
67
+    def set_ssh_keys(self,ssh_key1,ssh_key2,ssh_key3,dbnames):
68
+        self.set_remote(data={
69
+            'ssh_key1': ssh_key1,
70
+            'ssh_key2': ssh_key2,
71
+            'ssh_key3': ssh_key3,	
72
+            'dbnames': dbnames,
73
+        })
74
+        print("In set_ssh_keys",ssh_key1,ssh_key2,ssh_key3,dbnames)
75
+
76
+
77
+
78
+ 
Back to file index

hooks/relations/was-ihs/README.md

 1
--- 
 2
+++ hooks/relations/was-ihs/README.md
 3
@@ -0,0 +1,39 @@
 4
+
 5
+Overview
 6
+-----------
 7
+
 8
+This interface layer handles the communication between a `IBM WAS Base` or `IBM WAS ND` and `IBM Http Server` charms.
 9
+The provider end of this interface provides the WAS Base or WAS ND service. The consumer/Requires part will be the IBM Http Server which wants the WAS Base/ND installation path, profile name and other information to configure the IBM HTTP Server and WebSphere Plugin for WebSphere Application Server.
10
+
11
+
12
+Usage
13
+------
14
+
15
+#### Provides
16
+IBM WebSphere Application Server product will provide this interface. This interface layer will set the following states, as appropriate:
17
+
18
+ - `{relation_name}.available`: The relation is established, WAS Base/WAS ND service is ready to send it's information.
19
+
20
+ - `{relation_name}.departed` : The relation has been removed. Any cleanup related to the consumer charm(IBM HTTP Server) should happen now on the IBM WAS Base/WAS ND charm since the consumer is going away.
21
+
22
+
23
+#### Requires
24
+
25
+Consumer charm `IBM HTTP Server` will require this interface to connect to WAS Base or WAS ND so that they can share information to configure WebSphere plug-in and to create web server definition. This interface layer will set the following states, as appropriate:
26
+
27
+- `{relation_name}.available` : The consumer charm has been related to a WAS Base/WAS ND provider charm. At this point, the charm waits for Provider charm to send configuration details like installation path, profile name, admin userid, admin password, admin group, host name, port and IP address.
28
+
29
+- `{relation_name}.ready` : IBM Http Server is now ready to access the configuration details provided by WAS Base/WAS ND charm(s). The Http Server charm can access the configuration details using the below methods:
30
+
31
+    - `get_was_path()` - returns the Installation path of WAS.
32
+    - `get_was_port()` - returns running port of WAS.
33
+    - `get_was_hostname()` - returns host name of WAS.
34
+    - `get_ihsadminusr()` - returns IHS admin username to be created.
35
+    - `get_ihsadmingrp()` - returns IHS admin group to be created.
36
+    - `get_ihsadminpw()` - returns IHS admin password to be created.
37
+    - `get_was_profile_name()` - returns the WAS profile name.
38
+    -  `set_isihsstarted()` - This function is called by IBM Htp Server charm to send message to the WAS Base/WAS ND charm.
39
+
40
+Once the Http Server started, send the message `started` to the WAS to restart the application server. 
41
+
42
+- `{relation_name}.departed` : The relation has been removed. Any cleanup related to the provider charm(IBM WAS Base/WAS ND) should happen now on the IBM Http Server charm since the relation is broken.
Back to file index

hooks/relations/was-ihs/interface.yaml

1
--- 
2
+++ hooks/relations/was-ihs/interface.yaml
3
@@ -0,0 +1,5 @@
4
+name: was-ihs
5
+summary: Interface to connect IBM WAS Base/IBM WAS ND and IBM Http Server charms
6
+maintainer: IBM Juju Support Team <jujusupp@us.ibm.com>
7
+version: 1
8
+#repo: ""
Back to file index

hooks/relations/was-ihs/provides.py

 1
--- 
 2
+++ hooks/relations/was-ihs/provides.py
 3
@@ -0,0 +1,55 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class wasProvides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.GLOBAL
12
+
13
+    @hook('{provides:was-ihs}-relation-{joined,changed}')
14
+    def changed(self):
15
+        conversation = self.conversation()
16
+        conversation.set_state('{relation_name}.available')
17
+        conversation.remove_state('{relation_name}.departed')
18
+
19
+    @hook('{provides:was-ihs}-relation-{broken,departed}')
20
+    def broken(self):
21
+        conversation = self.conversation()
22
+        conversation.remove_state('{relation_name}.available')
23
+        conversation.set_state('{relation_name}.departed')
24
+
25
+    def dismiss(self, service):
26
+        conversation = self.conversation(service)
27
+        conversation.remove_state('{relation_name}.departed')
28
+
29
+    def reset_states(self, service):
30
+        conversation = self.conversation(service)
31
+        conversation.remove_state('{relation_name}.available')
32
+        conversation.remove_state('{relation_name}.departed')
33
+
34
+    def set_was_details(self, service, was_path, was_port, hostname,
35
+                        ihsadminusr, ihsadmingrp, ihsadminpw, profilename):
36
+        conversation = self.conversation(service)
37
+        conversation.set_remote(data={
38
+            'was_path': was_path,
39
+            'was_port': was_port,
40
+            'hostname': hostname,
41
+            'ihsadminusr': ihsadminusr,
42
+            'ihsadmingrp': ihsadmingrp,
43
+            'ihsadminpw': ihsadminpw,
44
+            'profilename': profilename
45
+        })
46
+
47
+    def get_isihsstarted(self, service):
48
+        conversation = self.conversation(service)
49
+        return conversation.get_remote('isihsstarted')
50
+
51
+    def services(self):
52
+        """
53
+        Return a list of services connecting to WAS.
54
+        """
55
+        service = []
56
+        for conversation in self.conversations():
57
+            service.append(conversation.scope)
58
+        return service
Back to file index

hooks/relations/was-ihs/requires.py

 1
--- 
 2
+++ hooks/relations/was-ihs/requires.py
 3
@@ -0,0 +1,52 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class ihsRequires(RelationBase):
10
+    scope = scopes.GLOBAL
11
+
12
+    auto_accessors = ['was_path', 'was_port', 'hostname', 'ihsadminusr',
13
+                      'ihsadmingrp', 'ihsadminpw', 'profilename',
14
+                      'isihsadminstarted']
15
+
16
+    @hook('{requires:was-ihs}-relation-joined')
17
+    def joined(self):
18
+        self.remove_state('{relation_name}.departed')
19
+        self.set_state('{relation_name}.available')
20
+
21
+    @hook('{requires:was-ihs}-relation-changed')
22
+    def changed(self):
23
+        self.remove_state('{relation_name}.departed')
24
+        if str(self.get_remote('was_port')) != "None":
25
+            self.set_state('{relation_name}.ready')
26
+
27
+    @hook('{requires:was-ihs}-relation-departed')
28
+    def departed(self):
29
+        self.remove_state('{relation_name}.available')
30
+        self.remove_state('{relation_name}.ready')
31
+        self.set_state('{relation_name}.departed')
32
+
33
+    def get_was_path(self):
34
+        return self.get_remote('was_path')
35
+
36
+    def get_was_port(self):
37
+        return self.get_remote('was_port')
38
+
39
+    def get_was_hostname(self):
40
+        return self.get_remote('hostname')
41
+
42
+    def get_ihsadminusr(self):
43
+        return self.get_remote('ihsadminusr')
44
+
45
+    def get_ihsadmingrp(self):
46
+        return self.get_remote('ihsadmingrp')
47
+
48
+    def get_ihsadminpw(self):
49
+        return self.get_remote('ihsadminpw')
50
+
51
+    def get_was_profile_name(self):
52
+        return self.get_remote('profilename')
53
+
54
+    def set_isihsstarted(self, isihsstarted):
55
+        self.set_remote('isihsstarted', isihsstarted)
Back to file index

hooks/start

 1
--- 
 2
+++ hooks/start
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/stop

 1
--- 
 2
+++ hooks/stop
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/update-status

 1
--- 
 2
+++ hooks/update-status
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/upgrade-charm

 1
--- 
 2
+++ hooks/upgrade-charm
 3
@@ -0,0 +1,28 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import os
 8
+import sys
 9
+sys.path.append('lib')
10
+
11
+# This is an upgrade-charm context, make sure we install latest deps
12
+if not os.path.exists('wheelhouse/.upgrade'):
13
+    open('wheelhouse/.upgrade', 'w').close()
14
+    if os.path.exists('wheelhouse/.bootstrapped'):
15
+        os.unlink('wheelhouse/.bootstrapped')
16
+else:
17
+    os.unlink('wheelhouse/.upgrade')
18
+
19
+from charms.layer import basic
20
+basic.bootstrap_charm_deps()
21
+basic.init_config_states()
22
+
23
+
24
+# This will load and run the appropriate @hook and other decorated
25
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
26
+# and $CHARM_DIR/hooks/relations.
27
+#
28
+# See https://jujucharms.com/docs/stable/authors-charm-building
29
+# for more information on this pattern.
30
+from charms.reactive import main
31
+main()
Back to file index

hooks/was-ihs-relation-broken

 1
--- 
 2
+++ hooks/was-ihs-relation-broken
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/was-ihs-relation-changed

 1
--- 
 2
+++ hooks/was-ihs-relation-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/was-ihs-relation-departed

 1
--- 
 2
+++ hooks/was-ihs-relation-departed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/was-ihs-relation-joined

 1
--- 
 2
+++ hooks/was-ihs-relation-joined
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasdb-relation-broken

 1
--- 
 2
+++ hooks/wasdb-relation-broken
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasdb-relation-changed

 1
--- 
 2
+++ hooks/wasdb-relation-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasdb-relation-departed

 1
--- 
 2
+++ hooks/wasdb-relation-departed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasdb-relation-joined

 1
--- 
 2
+++ hooks/wasdb-relation-joined
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasmessaging-relation-broken

 1
--- 
 2
+++ hooks/wasmessaging-relation-broken
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasmessaging-relation-changed

 1
--- 
 2
+++ hooks/wasmessaging-relation-changed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasmessaging-relation-departed

 1
--- 
 2
+++ hooks/wasmessaging-relation-departed
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

hooks/wasmessaging-relation-joined

 1
--- 
 2
+++ hooks/wasmessaging-relation-joined
 3
@@ -0,0 +1,19 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+# Load modules from $CHARM_DIR/lib
 7
+import sys
 8
+sys.path.append('lib')
 9
+
10
+from charms.layer import basic
11
+basic.bootstrap_charm_deps()
12
+basic.init_config_states()
13
+
14
+
15
+# This will load and run the appropriate @hook and other decorated
16
+# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive,
17
+# and $CHARM_DIR/hooks/relations.
18
+#
19
+# See https://jujucharms.com/docs/stable/authors-charm-building
20
+# for more information on this pattern.
21
+from charms.reactive import main
22
+main()
Back to file index

icon.svg

 1
--- 
 2
+++ icon.svg
 3
@@ -0,0 +1,53 @@
 4
+<?xml version="1.0" encoding="UTF-8"?>
 5
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 6
+<!-- Creator: CorelDRAW X6 -->
 7
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1in" height="0.999996in" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
 8
+viewBox="0 0 1116 1116"
 9
+ xmlns:xlink="http://www.w3.org/1999/xlink">
10
+ <defs>
11
+   <linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="558.139" y1="0" x2="558.139" y2="1116.27">
12
+    <stop offset="0" stop-color="#BFA6E6"/>
13
+    <stop offset="1" stop-color="#6C5393"/>
14
+   </linearGradient>
15
+   <mask id="id1">
16
+     <linearGradient id="id2" gradientUnits="userSpaceOnUse" x1="558.139" y1="65.2803" x2="558.139" y2="342.715">
17
+      <stop offset="0" stop-opacity="1" stop-color="white"/>
18
+      <stop offset="0.141176" stop-opacity="-10.5451" stop-color="white"/>
19
+      <stop offset="1" stop-opacity="0" stop-color="white"/>
20
+     </linearGradient>
21
+    <rect fill="url(#id2)" width="1116" height="408"/>
22
+   </mask>
23
+ </defs>
24
+ <g id="Layer_x0020_1">
25
+  <metadata id="CorelCorpID_0Corel-Layer"/>
26
+  <path id="Background" fill="url(#id0)" d="M0 754l0 -392c0,-317 45,-362 362,-362l393 0c316,0 361,45 361,362l0 392c0,317 -45,362 -361,362l-393 0c-317,0 -362,-45 -362,-362z"/>
27
+  <path fill="#999999" mask="url(#id1)" d="M0 408l0 -46c0,-317 45,-362 362,-362l393 0c316,0 361,45 361,362l0 46c0,-317 -45,-362 -361,-362l-393 0c-317,0 -362,45 -362,362z"/>
28
+  <g id="_195711952">
29
+   <path fill="#CCCCCC" fill-rule="nonzero" d="M426 711l-54 0 -54 -195 -55 195 -53 0 -71 -249 -29 0 0 -47 120 0 0 47 -32 0 41 156 54 -203 48 0 57 201 39 -154 -33 0 0 -47 121 0 0 47 -33 0 -66 249zm210 -231l-1 0 -37 102 75 0 -37 -102zm36 190l33 0 -18 -44 -104 0 -17 44 32 0 0 41 -121 0 0 -47 32 0 86 -202 -33 0 0 -47 100 0 103 249 32 0 0 47 -125 0 0 -41zm201 -47c0,7 1,12 3,18 2,5 5,10 8,14 4,5 9,8 14,10 5,3 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-13 -5,-3 -10,-5 -16,-7 -6,-2 -12,-4 -18,-6 -10,-3 -20,-6 -30,-9 -9,-4 -19,-9 -28,-16 -21,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-8 18,-13 29,-18 10,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18l0 -17 44 0 0 80 -51 0c0,-11 -4,-21 -11,-28 -8,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,5 7,9 12,12 4,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,10 10,4 20,10 30,17 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -8,37 -4,12 -11,21 -19,30 -8,8 -18,14 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -10,-4 -18,-10 -25,-19l0 19 -45 0 0 -88 52 0z"/>
30
+   <g>
31
+    <path fill="#C8C8C8" d="M425 711c-17,0 -35,0 -53,0 -18,-65 -37,-130 -55,-196 -18,66 -36,131 -55,196 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -10,0 -21,0 -32,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 15,0 31,0 47,0 19,67 38,134 57,201 13,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,68 -38,101 25,0 51,0 76,0 -13,-33 -25,-67 -38,-101zm37 189c10,0 21,0 32,0 -5,-14 -11,-29 -17,-44 -35,0 -70,0 -104,0 -6,15 -12,30 -17,44 10,0 21,0 32,0 0,14 0,28 0,42 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 10,0 21,0 31,0 0,15 0,31 0,47 -41,0 -83,0 -124,0 0,-14 0,-28 0,-42zm200 -46c0,6 1,12 3,17 2,6 5,11 9,15 4,4 8,7 14,10 5,2 11,3 18,3 12,0 23,-3 31,-11 8,-7 13,-17 13,-29 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-2 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -33,-38 -33,-65 0,-12 3,-24 8,-34 5,-11 11,-20 19,-27 9,-8 18,-14 29,-18 11,-4 22,-7 34,-7 10,0 20,2 29,6 10,3 18,9 24,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -8,7 -11,16 -11,28 0,7 1,13 4,17 3,5 7,9 11,12 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,16 22,27 5,11 7,22 7,35 0,13 -2,25 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -26,-20 0,7 0,13 0,20 -15,0 -29,0 -44,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
32
+    <path fill="#C5C5C5" d="M425 710c-18,0 -36,0 -54,0 -18,-65 -36,-130 -54,-195 -19,65 -37,130 -55,195 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -20,0 -29,0 0,-15 0,-31 0,-47 40,0 80,0 120,0 0,16 0,32 0,47 -11,0 -22,0 -32,0 14,52 28,104 42,156 17,-68 35,-135 53,-203 16,0 32,0 48,0 19,67 38,134 57,201 13,-51 26,-102 40,-154 -12,0 -23,0 -34,0 0,-15 0,-31 0,-47 40,0 81,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -26,68 -38,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-44 -34,0 -69,0 -104,0 -5,14 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,41 -40,0 -80,0 -121,0 0,-15 0,-31 0,-47 11,0 21,0 32,0 29,-67 57,-134 86,-202 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 34,0 67,0 100,0 34,83 69,166 103,249 11,0 22,0 32,0 0,16 0,32 0,47 -42,0 -83,0 -125,0 0,-13 0,-27 0,-41zm201 -46c0,6 1,11 3,17 2,5 5,10 8,14 4,5 9,8 14,10 5,3 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-12 -4,-4 -10,-6 -16,-8 -6,-2 -12,-4 -17,-6 -11,-3 -21,-6 -31,-9 -9,-4 -18,-9 -28,-16 -21,-16 -32,-37 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 10,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 44,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,16 -12,27 0,7 2,13 4,18 3,5 7,9 12,12 5,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,10 10,4 20,10 30,17 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,21 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-87 17,0 34,0 52,0z"/>
33
+    <path fill="#C1C1C1" d="M424 710c-17,0 -35,0 -53,0 -18,-65 -37,-130 -55,-195 -18,65 -36,130 -55,195 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 121,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 16,0 31,0 47,0 19,67 38,134 57,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,68 -38,102 25,0 51,0 76,0 -13,-34 -25,-68 -38,-102zm37 190c11,0 21,0 32,0 -5,-15 -11,-30 -17,-45 -35,0 -69,0 -104,0 -6,15 -12,30 -17,45 10,0 21,0 32,0 0,13 0,27 0,41 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 11,0 21,0 32,0 0,15 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-28 0,-41zm200 -47c0,6 1,12 3,17 2,6 5,11 9,15 4,4 8,7 14,10 5,2 11,3 18,3 13,0 23,-3 31,-11 9,-7 13,-17 13,-29 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-2 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -32,-38 -32,-65 0,-12 2,-24 7,-34 5,-11 11,-20 20,-27 8,-8 17,-14 28,-18 11,-4 22,-7 34,-7 10,0 20,2 29,6 10,3 18,9 24,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -7,7 -11,16 -11,28 0,7 1,13 4,18 3,4 7,8 11,11 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,17 22,27 5,11 7,22 7,35 0,13 -2,25 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -26,-19 0,6 0,13 0,19 -14,0 -29,0 -44,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
34
+    <path fill="#BEBEBE" d="M424 710c-18,0 -36,0 -54,0 -18,-66 -36,-131 -54,-196 -19,65 -37,130 -55,196 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -19,0 -29,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -22,0 -32,0 14,51 28,103 42,155 17,-67 35,-135 53,-203 16,0 32,0 48,0 19,67 38,134 57,202 13,-52 26,-103 40,-154 -11,0 -23,0 -34,0 0,-16 0,-32 0,-48 41,0 81,0 121,0 0,16 0,32 0,48 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -232c0,0 0,0 0,0 -13,34 -26,68 -38,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-44 -34,0 -69,0 -104,0 -5,14 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,42 -40,0 -80,0 -121,0 0,-16 0,-32 0,-48 11,0 21,0 32,0 29,-67 58,-134 86,-201 -11,0 -22,0 -32,0 0,-16 0,-32 0,-48 33,0 66,0 99,0 34,83 69,166 104,249 10,0 21,0 31,0 0,16 0,32 0,48 -42,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,11 3,17 2,6 5,10 8,15 4,4 9,7 14,9 6,3 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-12 -4,-4 -10,-6 -16,-8 -6,-2 -12,-4 -17,-5 -11,-4 -21,-7 -31,-10 -9,-4 -18,-9 -28,-16 -21,-16 -32,-37 -32,-65 0,-12 3,-23 7,-34 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 11,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 44,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -17,-11 -28,-11 -11,0 -20,4 -28,10 -8,7 -12,16 -12,27 0,7 2,13 4,18 3,5 7,9 12,12 5,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,11 10,3 21,9 30,16 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,22 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-10 -25,-19 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 17,0 34,0 52,0z"/>
35
+    <path fill="#BABABA" d="M424 709c-18,0 -36,0 -54,0 -18,-65 -37,-130 -55,-195 -18,65 -36,130 -55,195 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 121,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 16,0 32,0 47,0 19,67 38,134 57,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -32,0 -22,83 -44,166 -66,249zm210 -231c-1,0 -1,0 -1,0 -12,34 -25,68 -38,102 26,0 51,0 76,0 -13,-34 -25,-68 -37,-102zm36 190c11,0 22,0 32,0 -5,-15 -11,-30 -17,-45 -35,0 -69,0 -104,0 -6,15 -12,30 -17,45 10,0 21,0 32,0 0,13 0,27 0,41 -40,0 -81,0 -121,0 0,-16 0,-31 0,-47 11,0 21,0 32,0 28,-67 57,-135 86,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 11,0 21,0 32,0 0,16 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-28 0,-41zm200 -47c0,6 1,12 3,18 2,5 5,10 9,14 4,4 8,7 14,10 5,2 11,3 18,3 13,0 23,-3 31,-10 9,-8 13,-18 13,-30 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-1 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -32,-38 -32,-65 0,-12 2,-24 7,-34 5,-11 11,-20 20,-27 8,-8 17,-14 28,-18 11,-4 22,-7 34,-7 11,0 20,2 30,6 9,3 17,9 23,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -50,0 -1,-11 -4,-20 -12,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,10 -7,6 -11,15 -11,27 0,7 1,13 4,18 3,4 7,8 11,11 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,17 22,27 5,11 7,22 7,35 0,13 -2,26 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,5 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
36
+    <path fill="#B6B6B6" d="M423 709c-18,0 -36,0 -53,0 -19,-66 -37,-131 -55,-196 -19,65 -37,130 -55,196 -18,0 -35,0 -53,0 -23,-83 -47,-166 -71,-249 -10,0 -19,0 -29,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -21,0 -32,0 14,51 28,103 42,155 18,-67 35,-135 53,-203 16,0 32,0 48,0 19,67 38,134 57,202 13,-52 26,-103 40,-154 -11,0 -22,0 -34,0 0,-16 0,-32 0,-48 41,0 81,0 121,0 0,16 0,32 0,48 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -232c0,0 0,0 0,0 -13,34 -26,68 -38,102 25,0 50,0 75,0 -12,-34 -24,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-29 -17,-44 -35,0 -70,0 -105,0 -5,15 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,42 -40,0 -80,0 -120,0 0,-16 0,-32 0,-48 10,0 21,0 31,0 29,-67 58,-134 86,-201 -11,0 -21,0 -32,0 0,-16 0,-32 0,-48 33,0 66,0 99,0 34,83 69,166 104,249 10,0 21,0 31,0 0,16 0,32 0,48 -41,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,12 3,17 2,6 5,10 9,15 3,4 8,7 13,9 6,3 12,4 19,4 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -4,-5 -8,-9 -13,-12 -4,-3 -10,-6 -16,-8 -6,-2 -12,-4 -17,-5 -11,-3 -21,-7 -31,-10 -9,-4 -18,-9 -27,-16 -22,-16 -33,-37 -33,-64 0,-13 3,-24 7,-35 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 11,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 24,18 0,-6 0,-11 0,-17 14,0 28,0 43,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -16,-11 -28,-11 -11,0 -20,4 -28,10 -8,7 -12,16 -12,27 0,7 2,13 5,18 2,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 11,3 22,7 33,11 10,3 21,9 30,16 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,22 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-10 -25,-19 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 17,0 35,0 52,0z"/>
37
+    <path fill="#B3B3B3" d="M423 708c-18,0 -36,0 -54,0 -18,-65 -36,-130 -55,-195 -18,65 -36,130 -55,195 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-31 0,-47 40,0 81,0 121,0 0,16 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 16,0 32,0 47,0 19,67 38,134 57,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 40,0 80,0 120,0 0,16 0,31 0,47 -11,0 -21,0 -32,0 -22,83 -44,166 -66,249zm210 -231c-1,0 -1,0 -1,0 -12,34 -25,68 -38,102 26,0 51,0 76,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-45 -35,0 -69,0 -104,0 -6,15 -11,30 -17,45 11,0 21,0 32,0 0,13 0,27 0,41 -40,0 -81,0 -121,0 0,-16 0,-31 0,-47 11,0 21,0 32,0 28,-67 57,-135 86,-202 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 33,0 66,0 99,0 35,83 70,166 104,249 11,0 21,0 32,0 0,16 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-28 0,-41zm200 -47c0,6 1,12 3,18 2,5 5,10 9,14 4,4 8,8 14,10 5,2 11,3 18,3 13,0 23,-3 31,-10 9,-8 13,-18 13,-30 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-7 -6,-2 -12,-4 -18,-6 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 11,-20 20,-27 8,-8 18,-14 28,-18 11,-4 22,-6 34,-6 11,0 20,1 30,5 9,4 17,9 23,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -50,0 -1,-11 -4,-20 -12,-27 -7,-7 -16,-11 -27,-11 -11,0 -21,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,4 7,8 12,11 4,3 9,6 15,8 6,1 11,3 17,5 12,3 23,6 34,10 10,4 20,10 30,17 9,7 17,17 22,27 5,11 7,23 7,35 0,13 -2,26 -7,37 -4,11 -11,21 -19,29 -9,9 -18,15 -30,20 -11,5 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
38
+    <path fill="#AFAFAF" d="M422 708c-18,0 -36,0 -53,0 -19,-65 -37,-131 -55,-196 -18,65 -37,131 -55,196 -18,0 -35,0 -53,0 -23,-83 -47,-166 -71,-249 -10,0 -19,0 -29,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -21,0 -32,0 14,52 28,103 42,155 18,-67 35,-135 53,-203 16,0 32,0 48,0 19,67 38,135 57,202 13,-52 26,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -232c0,0 0,0 0,0 -13,34 -25,68 -38,102 25,0 50,0 76,0 -13,-34 -25,-68 -38,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-29 -17,-44 -35,0 -70,0 -105,0 -5,15 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,42 -40,0 -80,0 -120,0 0,-16 0,-32 0,-48 10,0 21,0 31,0 29,-67 58,-134 86,-201 -10,0 -21,0 -32,0 0,-16 0,-32 0,-48 33,0 66,0 99,0 34,83 69,166 104,249 10,0 21,0 31,0 0,16 0,32 0,48 -41,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,12 3,17 2,6 5,11 9,15 3,4 8,7 13,9 6,3 12,4 19,4 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -4,-5 -8,-9 -12,-12 -5,-3 -11,-6 -17,-8 -6,-2 -11,-4 -17,-5 -11,-3 -21,-7 -31,-10 -9,-4 -18,-9 -27,-16 -22,-16 -33,-37 -33,-64 0,-13 3,-24 7,-34 5,-11 12,-20 20,-28 9,-7 18,-13 29,-17 11,-5 22,-7 34,-7 10,0 20,2 29,5 9,4 17,10 24,18 0,-6 0,-11 0,-17 14,0 28,0 43,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -16,-11 -27,-11 -12,0 -21,4 -29,10 -8,7 -12,16 -12,27 0,8 2,13 5,18 3,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 11,3 23,7 33,11 11,4 21,9 30,16 10,8 17,17 22,28 5,11 8,22 8,35 0,13 -3,25 -7,36 -5,12 -11,22 -20,30 -8,8 -18,15 -29,19 -12,5 -24,8 -38,8 -12,0 -22,-3 -32,-7 -9,-4 -18,-10 -25,-19 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 17,0 35,0 52,0z"/>
39
+    <path fill="#ABABAB" d="M422 707c-18,0 -36,0 -54,0 -18,-65 -36,-130 -55,-195 -18,65 -36,130 -54,195 -18,0 -36,0 -53,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-31 0,-47 40,0 81,0 121,0 0,16 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-135 54,-203 16,0 32,0 48,0 19,67 37,134 56,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 40,0 80,0 120,0 0,16 0,31 0,47 -10,0 -21,0 -32,0 -22,83 -44,166 -66,249zm210 -231c0,0 -1,0 -1,0 -12,34 -25,68 -37,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-45 -35,0 -69,0 -104,0 -6,15 -11,30 -17,45 11,0 21,0 32,0 0,14 0,27 0,41 -40,0 -81,0 -121,0 0,-16 0,-31 0,-47 11,0 21,0 32,0 28,-67 57,-135 86,-202 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 33,0 66,0 99,0 35,83 70,166 104,249 11,0 21,0 32,0 0,16 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-27 0,-41zm200 -47c0,6 1,12 3,18 2,5 5,10 9,14 4,4 8,8 14,10 5,2 11,3 18,3 13,0 23,-3 31,-10 9,-8 13,-17 13,-30 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-5 -16,-7 -6,-2 -12,-4 -18,-6 -11,-3 -21,-6 -30,-10 -9,-3 -19,-8 -28,-15 -21,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 11,-19 20,-27 8,-8 18,-14 28,-18 11,-4 23,-6 34,-6 11,0 20,1 30,5 9,4 17,9 23,17 0,-5 0,-11 0,-16 15,0 29,0 43,0 0,26 0,53 0,80 -17,0 -33,0 -50,0 -1,-12 -4,-21 -12,-28 -7,-7 -16,-11 -27,-11 -11,0 -20,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,4 7,8 12,11 4,3 9,6 15,8 6,1 12,3 17,5 12,3 23,6 34,10 10,4 20,10 30,17 10,8 17,17 22,27 5,11 7,23 7,35 0,13 -2,26 -7,37 -4,11 -11,21 -19,29 -9,9 -18,15 -30,20 -11,5 -24,7 -37,7 -12,0 -23,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-88 17,0 34,0 51,0z"/>
40
+    <path fill="#A8A8A8" d="M421 707c-18,0 -36,0 -53,0 -19,-65 -37,-131 -55,-196 -18,65 -37,131 -55,196 -18,0 -35,0 -53,0 -23,-83 -47,-166 -71,-249 -9,0 -19,0 -29,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -21,0 -32,0 14,52 28,103 42,155 18,-67 36,-135 53,-203 16,0 32,0 48,0 19,67 38,135 57,202 13,-52 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,33 -25,67 -38,101 25,0 50,0 76,0 -13,-34 -25,-68 -38,-101zm36 189c11,0 22,0 33,0 -6,-15 -11,-29 -17,-44 -35,0 -70,0 -104,0 -6,15 -12,29 -18,44 11,0 22,0 32,0 0,14 0,28 0,42 -40,0 -80,0 -120,0 0,-16 0,-32 0,-48 10,0 21,0 31,0 29,-67 58,-134 87,-201 -11,0 -22,0 -33,0 0,-16 0,-32 0,-48 33,0 66,0 99,0 35,83 69,166 104,249 10,0 21,0 31,0 0,16 0,32 0,48 -41,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,12 3,17 2,6 5,11 9,15 3,4 8,7 13,9 6,3 12,4 19,4 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -4,-5 -8,-9 -12,-12 -5,-3 -11,-6 -17,-8 -6,-2 -11,-4 -17,-5 -11,-3 -21,-7 -31,-10 -9,-3 -18,-9 -27,-16 -22,-16 -33,-37 -33,-64 0,-13 3,-24 8,-34 4,-11 11,-20 19,-28 9,-7 18,-13 29,-17 11,-5 22,-7 34,-7 10,0 20,2 29,6 9,3 17,9 24,17 0,-6 0,-11 0,-17 14,0 28,0 43,0 0,27 0,54 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -16,-11 -27,-11 -11,0 -21,4 -29,10 -8,7 -12,16 -12,27 0,8 2,14 5,18 3,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 12,3 23,7 33,11 11,4 21,9 30,16 10,8 17,17 22,28 5,11 8,22 8,35 0,13 -2,25 -7,37 -5,11 -11,21 -20,29 -8,8 -18,15 -29,20 -12,4 -24,7 -38,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-11 -25,-20 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 18,0 35,0 52,0z"/>
41
+    <path fill="#A4A4A4" d="M421 706c-18,0 -36,0 -54,0 -18,-65 -36,-130 -55,-195 -18,65 -36,130 -54,195 -18,0 -36,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-31 0,-47 41,0 81,0 121,0 0,16 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-135 54,-203 16,0 32,0 48,0 19,67 38,134 56,201 14,-51 27,-102 40,-154 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 40,0 80,0 121,0 0,16 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 -1,0 -1,0 -12,34 -25,68 -37,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-45 -34,0 -69,0 -104,0 -6,15 -11,30 -17,45 11,0 21,0 32,0 0,14 0,27 0,41 -40,0 -81,0 -121,0 0,-15 0,-31 0,-47 11,0 21,0 32,0 29,-67 57,-135 86,-202 -11,0 -22,0 -33,0 0,-16 0,-31 0,-47 33,0 66,0 99,0 35,83 70,166 104,249 11,0 21,0 32,0 0,16 0,32 0,47 -42,0 -83,0 -125,0 0,-14 0,-27 0,-41zm200 -47c0,6 1,12 3,18 3,5 5,10 9,14 4,4 9,8 14,10 5,2 11,4 18,4 13,0 23,-4 32,-11 8,-7 12,-17 12,-30 0,-8 -2,-14 -5,-19 -3,-4 -7,-8 -12,-12 -5,-3 -10,-5 -16,-7 -6,-2 -12,-4 -18,-6 -10,-3 -21,-6 -30,-9 -9,-4 -19,-9 -28,-16 -21,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-8 18,-14 28,-18 11,-4 23,-6 34,-6 11,0 20,1 30,5 9,4 17,9 23,18 0,-6 0,-12 0,-17 15,0 29,0 43,0 0,26 0,53 0,80 -17,0 -33,0 -50,0 -1,-11 -4,-21 -11,-28 -8,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,4 7,8 12,11 4,3 10,6 15,8 6,1 12,3 17,5 12,3 23,6 34,10 10,4 20,10 30,17 10,8 17,17 22,27 5,11 7,23 7,35 0,13 -2,26 -7,37 -4,11 -11,21 -19,30 -8,8 -18,14 -30,19 -11,5 -24,7 -37,7 -12,0 -23,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-88 17,0 34,0 51,0z"/>
42
+    <path fill="#A1A1A1" d="M420 706c-18,0 -35,0 -53,0 -19,-65 -37,-131 -55,-196 -18,65 -37,131 -55,196 -17,0 -35,0 -53,0 -23,-83 -47,-166 -71,-249 -9,0 -19,0 -29,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -10,0 -21,0 -32,0 14,52 28,104 42,155 18,-67 36,-135 53,-203 16,0 32,0 48,0 19,68 38,135 57,202 13,-52 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-48 40,0 80,0 120,0 0,16 0,32 0,48 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,67 -38,101 25,0 50,0 76,0 -13,-34 -25,-67 -38,-101zm36 189c11,0 22,0 33,0 -6,-14 -11,-29 -17,-44 -35,0 -70,0 -104,0 -6,15 -12,30 -17,44 10,0 21,0 32,0 0,14 0,28 0,42 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-48 33,0 66,0 99,0 35,83 69,166 104,250 10,0 21,0 31,0 0,15 0,31 0,47 -41,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,12 3,17 2,6 5,11 9,15 3,4 8,7 13,10 6,2 12,3 19,3 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -3,-5 -7,-9 -12,-12 -5,-3 -11,-6 -17,-8 -5,-2 -11,-4 -17,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-9 -28,-16 -22,-16 -33,-37 -33,-64 0,-12 3,-24 8,-34 4,-11 11,-20 19,-27 9,-8 18,-14 29,-18 11,-5 22,-7 34,-7 10,0 20,2 29,6 10,3 17,9 24,17 0,-5 0,-11 0,-17 14,0 29,0 43,0 0,27 0,54 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -8,7 -12,16 -12,27 0,8 2,14 5,18 3,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 12,3 23,7 33,11 11,4 21,9 30,16 10,8 17,17 22,28 5,11 8,22 8,35 0,13 -2,25 -7,37 -5,11 -11,21 -20,29 -8,8 -18,15 -29,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -32,-6 -9,-4 -18,-11 -25,-20 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 18,0 35,0 52,0z"/>
43
+    <path fill="#9D9D9D" d="M420 705c-18,0 -36,0 -54,0 -18,-65 -36,-130 -55,-195 -18,65 -36,130 -54,195 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -20,0 -30,0 0,-15 0,-31 0,-47 41,0 81,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-135 54,-203 16,0 32,0 48,0 19,67 38,134 57,201 13,-51 26,-102 39,-154 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 40,0 80,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 -1,0 -12,34 -25,68 -37,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-45 -34,0 -69,0 -104,0 -6,15 -11,30 -17,45 11,0 21,0 32,0 0,14 0,28 0,41 -40,0 -80,0 -121,0 0,-15 0,-31 0,-47 11,0 21,0 32,0 29,-67 57,-134 86,-202 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 33,0 66,0 100,0 34,83 69,166 103,249 11,0 21,0 32,0 0,16 0,32 0,47 -42,0 -83,0 -125,0 0,-13 0,-27 0,-41zm200 -47c0,6 1,12 4,18 2,5 5,10 8,14 4,4 9,8 14,10 5,2 12,4 18,4 13,0 23,-4 32,-11 8,-7 12,-17 12,-30 0,-8 -2,-14 -5,-19 -3,-4 -7,-8 -12,-12 -5,-3 -10,-5 -16,-7 -6,-2 -12,-4 -18,-6 -10,-3 -21,-6 -30,-9 -9,-4 -19,-9 -28,-16 -21,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-8 18,-14 29,-18 10,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 43,0 0,27 0,53 0,80 -16,0 -33,0 -50,0 0,-11 -4,-21 -11,-28 -8,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,5 7,8 12,11 4,4 10,6 15,8 6,2 12,3 17,5 12,3 23,6 34,10 10,4 20,10 30,17 10,8 17,17 22,28 5,10 7,22 7,34 0,13 -2,26 -7,37 -4,11 -11,21 -19,30 -8,8 -18,14 -30,19 -11,5 -24,7 -37,7 -12,0 -23,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-88 17,0 34,0 51,0z"/>
44
+    <path fill="#999999" d="M419 705c-18,0 -35,0 -53,0 -18,-65 -37,-130 -55,-196 -18,66 -37,131 -55,196 -17,0 -35,0 -53,0 -23,-83 -47,-166 -71,-249 -9,0 -19,0 -29,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -10,0 -21,0 -32,0 14,52 28,104 42,155 18,-67 36,-135 53,-202 16,0 32,0 48,0 19,67 38,134 57,201 13,-52 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,67 -38,101 25,0 51,0 76,0 -13,-34 -25,-67 -38,-101zm36 189c11,0 22,0 33,0 -6,-14 -11,-29 -17,-44 -35,0 -70,0 -104,0 -6,15 -12,30 -17,44 10,0 21,0 32,0 0,14 0,28 0,42 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 10,0 21,0 31,0 0,15 0,31 0,47 -41,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,12 3,17 2,6 5,11 9,15 4,4 8,7 13,10 6,2 12,3 19,3 12,0 23,-3 31,-11 8,-7 13,-17 13,-30 0,-7 -2,-13 -5,-18 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-2 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-9 -28,-15 -22,-17 -33,-38 -33,-65 0,-12 3,-24 8,-34 5,-11 11,-20 19,-27 9,-8 18,-14 29,-18 11,-5 22,-7 34,-7 10,0 20,2 29,6 10,3 17,9 24,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -8,7 -12,16 -12,28 0,7 2,13 5,17 3,5 7,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 12,4 23,7 33,11 11,4 21,9 31,16 9,8 17,17 22,28 5,11 7,22 7,35 0,13 -2,25 -7,37 -5,11 -11,21 -20,29 -8,8 -18,15 -29,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -32,-6 -9,-4 -17,-11 -25,-20 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-29 0,-59 0,-88 18,0 35,0 52,0z"/>
45
+    <path fill="#969696" d="M419 704c-18,0 -36,0 -54,0 -18,-65 -36,-130 -54,-195 -19,65 -37,130 -55,195 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -20,0 -29,0 0,-15 0,-31 0,-47 40,0 80,0 120,0 0,16 0,32 0,47 -11,0 -22,0 -32,0 14,52 27,104 41,156 18,-68 36,-135 54,-203 16,0 32,0 48,0 19,67 38,134 57,201 13,-51 26,-102 39,-154 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 40,0 81,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 -1,0 -12,34 -25,68 -37,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-44 -34,0 -69,0 -104,0 -5,14 -11,29 -17,44 11,0 21,0 32,0 0,14 0,28 0,41 -40,0 -80,0 -121,0 0,-15 0,-31 0,-47 11,0 21,0 32,0 29,-67 57,-134 86,-202 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 33,0 67,0 100,0 34,83 69,166 103,249 11,0 21,0 32,0 0,16 0,32 0,47 -42,0 -83,0 -125,0 0,-13 0,-27 0,-41zm201 -47c0,7 1,12 3,18 2,5 5,10 8,14 4,5 9,8 14,10 5,2 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-13 -5,-3 -10,-5 -16,-7 -6,-2 -12,-4 -18,-6 -10,-3 -20,-6 -30,-9 -9,-4 -19,-9 -28,-16 -21,-16 -32,-38 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-8 18,-13 29,-18 10,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 44,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-21 -11,-28 -8,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,15 -12,27 0,7 1,13 4,18 3,5 7,9 12,12 4,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,10 10,4 20,10 30,17 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -8,37 -4,12 -11,21 -19,30 -8,8 -18,14 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-88 17,0 34,0 52,0z"/>
46
+    <path fill="#929292" d="M418 704c-17,0 -35,0 -53,0 -18,-65 -37,-130 -55,-196 -18,66 -36,131 -55,196 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -10,0 -21,0 -32,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 15,0 31,0 47,0 19,67 38,134 57,201 13,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,68 -38,101 25,0 51,0 76,0 -13,-33 -25,-67 -38,-101zm37 189c10,0 21,0 32,0 -5,-14 -11,-29 -17,-44 -35,0 -70,0 -104,0 -6,15 -12,30 -17,44 10,0 21,0 32,0 0,14 0,28 0,42 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 10,0 21,0 31,0 0,15 0,31 0,47 -41,0 -83,0 -124,0 0,-14 0,-28 0,-42zm200 -46c0,6 1,12 3,17 2,6 5,11 9,15 4,4 8,7 14,10 5,2 11,3 18,3 12,0 23,-3 31,-11 8,-7 13,-17 13,-29 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-2 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -33,-38 -33,-65 0,-12 3,-24 8,-34 5,-11 11,-20 19,-27 9,-8 18,-14 29,-18 11,-5 22,-7 34,-7 10,0 20,2 29,6 10,3 18,9 24,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -8,7 -11,16 -11,28 0,7 1,13 4,17 3,5 7,9 11,12 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,16 22,27 5,11 7,22 7,35 0,13 -2,25 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -26,-20 0,7 0,13 0,20 -15,0 -29,0 -44,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
47
+    <path fill="#8E8E8E" d="M418 703c-18,0 -36,0 -54,0 -18,-65 -36,-130 -54,-195 -19,65 -37,130 -55,195 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-249 -10,0 -20,0 -29,0 0,-15 0,-31 0,-47 40,0 80,0 120,0 0,16 0,32 0,47 -11,0 -22,0 -32,0 14,52 28,104 42,156 17,-68 35,-135 53,-203 16,0 32,0 48,0 19,67 38,134 57,201 13,-51 26,-102 40,-154 -12,0 -23,0 -34,0 0,-15 0,-31 0,-47 40,0 81,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -26,68 -38,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-44 -34,0 -69,0 -104,0 -5,14 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,41 -40,0 -80,0 -121,0 0,-15 0,-31 0,-47 11,0 21,0 32,0 29,-67 57,-134 86,-202 -11,0 -22,0 -33,0 0,-15 0,-31 0,-47 34,0 67,0 100,0 34,83 69,166 103,249 11,0 22,0 32,0 0,16 0,32 0,47 -42,0 -83,0 -125,0 0,-13 0,-27 0,-41zm201 -47c0,7 1,12 3,18 2,5 5,10 8,14 4,5 9,8 14,10 5,3 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-12 -4,-4 -10,-6 -16,-8 -6,-2 -12,-4 -17,-6 -11,-3 -21,-6 -31,-9 -9,-4 -18,-9 -28,-16 -21,-16 -32,-37 -32,-65 0,-12 2,-23 7,-34 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 10,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 44,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -17,-11 -28,-11 -11,0 -20,3 -28,10 -8,6 -12,16 -12,27 0,7 2,13 4,18 3,5 7,9 12,12 5,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,10 10,4 20,10 30,17 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,21 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -10,-4 -18,-10 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-58 0,-88 17,0 34,0 52,0z"/>
48
+    <path fill="#8B8B8B" d="M417 703c-17,0 -35,0 -53,0 -18,-65 -37,-130 -55,-195 -18,65 -36,130 -55,195 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 121,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 16,0 31,0 47,0 19,67 38,134 57,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 -22,83 -44,166 -66,249zm210 -231c0,0 0,0 0,0 -13,34 -25,68 -38,102 25,0 51,0 76,0 -13,-34 -25,-68 -38,-102zm37 190c11,0 21,0 32,0 -5,-15 -11,-30 -17,-45 -35,0 -69,0 -104,0 -6,15 -12,30 -17,45 10,0 21,0 32,0 0,13 0,27 0,41 -41,0 -81,0 -121,0 0,-16 0,-32 0,-47 10,0 21,0 31,0 29,-68 58,-135 87,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 11,0 21,0 32,0 0,15 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-28 0,-41zm200 -47c0,6 1,12 3,17 2,6 5,11 9,15 4,4 8,7 14,10 5,2 11,3 18,3 13,0 23,-3 31,-11 9,-7 13,-17 13,-29 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-2 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -32,-38 -32,-65 0,-12 2,-24 7,-34 5,-11 11,-20 20,-27 8,-8 17,-14 28,-18 11,-4 22,-7 34,-7 10,0 20,2 29,6 10,3 18,9 24,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,9 -7,7 -11,16 -11,28 0,7 1,13 4,18 3,4 7,8 11,11 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,17 22,27 5,11 7,22 7,35 0,13 -2,25 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,4 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -26,-19 0,6 0,13 0,19 -14,0 -29,0 -44,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
49
+    <path fill="#878787" d="M417 703c-18,0 -36,0 -54,0 -18,-66 -36,-131 -54,-196 -19,65 -37,130 -55,196 -18,0 -35,0 -53,0 -24,-83 -47,-166 -71,-250 -10,0 -19,0 -29,0 0,-15 0,-31 0,-47 40,0 80,0 120,0 0,16 0,32 0,47 -11,0 -22,0 -32,0 14,52 28,104 42,156 17,-68 35,-135 53,-203 16,0 32,0 48,0 19,67 38,134 57,201 13,-51 26,-102 40,-154 -11,0 -23,0 -34,0 0,-15 0,-31 0,-47 41,0 81,0 121,0 0,16 0,32 0,47 -11,0 -22,0 -33,0 -22,84 -44,167 -66,250zm210 -232c0,0 0,0 0,0 -13,34 -26,68 -38,102 25,0 50,0 75,0 -12,-34 -25,-68 -37,-102zm36 190c11,0 22,0 33,0 -6,-15 -12,-30 -18,-44 -34,0 -69,0 -104,0 -5,14 -11,29 -17,44 11,0 22,0 32,0 0,14 0,28 0,42 -40,0 -80,0 -121,0 0,-16 0,-32 0,-48 11,0 21,0 32,0 29,-67 58,-134 86,-202 -11,0 -22,0 -32,0 0,-15 0,-31 0,-47 33,0 66,0 99,0 34,83 69,166 104,249 10,0 21,0 31,0 0,16 0,32 0,48 -42,0 -83,0 -125,0 0,-14 0,-28 0,-42zm201 -46c0,6 1,11 3,17 2,6 5,10 8,15 4,4 9,7 14,9 6,3 12,4 19,4 12,0 22,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-14 -5,-18 -3,-5 -7,-9 -12,-12 -4,-4 -10,-6 -16,-8 -6,-2 -12,-4 -17,-5 -11,-4 -21,-7 -31,-10 -9,-4 -18,-9 -28,-16 -21,-16 -32,-37 -32,-65 0,-12 3,-23 7,-34 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 11,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 23,18 0,-6 0,-11 0,-17 15,0 29,0 44,0 0,27 0,53 0,80 -17,0 -34,0 -51,0 0,-11 -4,-20 -11,-28 -7,-7 -17,-11 -28,-11 -11,0 -20,4 -28,10 -8,7 -12,16 -12,27 0,7 2,13 4,18 3,5 7,9 12,12 5,3 10,5 15,7 6,2 12,3 18,5 11,3 22,7 33,11 10,3 21,9 30,16 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,21 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-10 -25,-19 0,7 0,13 0,20 -15,0 -30,0 -45,0 0,-30 0,-59 0,-88 17,0 34,0 52,0z"/>
50
+    <path fill="#848484" d="M417 702c-18,0 -36,0 -54,0 -18,-65 -37,-130 -55,-195 -18,65 -36,130 -55,195 -17,0 -35,0 -52,0 -24,-83 -48,-166 -71,-249 -10,0 -20,0 -30,0 0,-16 0,-32 0,-47 40,0 80,0 121,0 0,15 0,31 0,47 -11,0 -22,0 -33,0 14,52 28,104 42,156 18,-68 36,-136 54,-203 16,0 32,0 47,0 19,67 38,134 57,201 14,-51 27,-103 40,-154 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 40,0 80,0 120,0 0,15 0,31 0,47 -11,0 -22,0 -32,0 -22,83 -44,166 -66,249zm210 -231c-1,0 -1,0 -1,0 -12,34 -25,68 -38,102 26,0 51,0 76,0 -13,-34 -25,-68 -37,-102zm36 190c11,0 22,0 32,0 -5,-15 -11,-30 -17,-45 -35,0 -69,0 -104,0 -6,15 -12,30 -17,45 10,0 21,0 32,0 0,13 0,27 0,41 -40,0 -81,0 -121,0 0,-16 0,-31 0,-47 11,0 21,0 32,0 28,-67 57,-135 86,-202 -11,0 -22,0 -33,0 0,-16 0,-32 0,-47 33,0 66,0 99,0 35,83 69,166 104,249 11,0 21,0 32,0 0,16 0,31 0,47 -42,0 -84,0 -125,0 0,-14 0,-28 0,-41zm200 -47c0,6 1,12 3,18 2,5 5,10 9,14 4,4 8,7 14,10 5,2 11,3 18,3 13,0 23,-3 31,-10 9,-8 13,-18 13,-30 0,-8 -2,-14 -5,-19 -3,-5 -7,-9 -12,-12 -5,-3 -10,-6 -16,-8 -6,-1 -12,-3 -18,-5 -11,-3 -21,-6 -30,-10 -10,-3 -19,-8 -28,-15 -22,-16 -32,-38 -32,-65 0,-12 2,-24 7,-34 5,-11 11,-20 20,-27 8,-8 17,-14 28,-18 11,-4 22,-7 34,-7 11,0 20,2 30,6 9,3 17,9 23,17 0,-5 0,-11 0,-16 14,0 29,0 43,0 0,26 0,53 0,79 -17,0 -34,0 -50,0 -1,-11 -4,-20 -12,-27 -7,-8 -16,-11 -27,-11 -11,0 -21,3 -29,10 -7,6 -11,15 -11,27 0,7 1,13 4,18 3,4 7,8 11,11 5,3 10,6 16,7 6,2 11,4 17,6 12,3 23,6 33,10 11,4 21,9 31,17 9,7 17,17 22,27 5,11 7,22 7,35 0,13 -2,26 -7,37 -5,11 -11,21 -19,29 -9,9 -19,15 -30,20 -12,5 -24,7 -38,7 -11,0 -22,-2 -31,-6 -10,-4 -18,-11 -25,-19 0,6 0,13 0,19 -15,0 -30,0 -45,0 0,-29 0,-59 0,-88 17,0 34,0 51,0z"/>
51
+   </g>
52
+   <path fill="gray" fill-rule="nonzero" d="M416 702l-54 0 -54 -196 -55 196 -53 0 -71 -249 -29 0 0 -48 120 0 0 48 -32 0 42 155 53 -203 48 0 57 202 40 -154 -34 0 0 -48 121 0 0 48 -33 0 -66 249zm210 -232l0 0 -38 102 75 0 -37 -102zm36 190l33 0 -17 -44 -105 0 -17 44 32 0 0 42 -120 0 0 -48 31 0 86 -201 -32 0 0 -48 99 0 104 249 31 0 0 48 -125 0 0 -42zm201 -46c0,6 1,12 3,17 2,6 5,10 9,15 3,4 8,7 13,9 6,3 12,4 19,4 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -4,-5 -8,-9 -13,-12 -4,-3 -10,-6 -16,-8 -6,-2 -12,-4 -17,-5 -11,-3 -21,-7 -31,-10 -9,-4 -18,-9 -27,-16 -22,-16 -33,-37 -33,-64 0,-13 3,-24 7,-35 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 11,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 24,18l0 -17 43 0 0 80 -51 0c0,-11 -4,-20 -11,-28 -7,-7 -16,-11 -28,-11 -11,0 -20,4 -28,10 -8,7 -12,16 -12,27 0,7 2,13 5,18 2,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 11,3 22,7 33,11 10,3 21,9 30,16 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,22 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-10 -25,-19l0 20 -45 0 0 -88 52 0z"/>
53
+   <path fill="white" fill-rule="nonzero" d="M416 702l-54 0 -54 -196 -55 196 -53 0 -71 -249 -29 0 0 -48 120 0 0 48 -32 0 42 155 53 -203 48 0 57 202 40 -154 -34 0 0 -48 121 0 0 48 -33 0 -66 249zm210 -232l0 0 -38 102 75 0 -37 -102zm36 190l33 0 -17 -44 -105 0 -17 44 32 0 0 42 -120 0 0 -48 31 0 86 -201 -32 0 0 -48 99 0 104 249 31 0 0 48 -125 0 0 -42zm201 -46c0,6 1,12 3,17 2,6 5,10 9,15 3,4 8,7 13,9 6,3 12,4 19,4 12,0 23,-4 31,-11 8,-7 12,-17 12,-30 0,-7 -1,-13 -4,-18 -4,-5 -8,-9 -13,-12 -4,-3 -10,-6 -16,-8 -6,-2 -12,-4 -17,-5 -11,-3 -21,-7 -31,-10 -9,-4 -18,-9 -27,-16 -22,-16 -33,-37 -33,-64 0,-13 3,-24 7,-35 5,-10 12,-19 20,-27 8,-7 18,-13 29,-18 11,-4 22,-6 34,-6 10,0 20,2 29,5 9,4 17,10 24,18l0 -17 43 0 0 80 -51 0c0,-11 -4,-20 -11,-28 -7,-7 -16,-11 -28,-11 -11,0 -20,4 -28,10 -8,7 -12,16 -12,27 0,7 2,13 5,18 2,5 6,9 11,12 5,3 10,5 16,7 5,2 11,4 17,5 11,3 22,7 33,11 10,3 21,9 30,16 10,8 17,17 22,28 5,10 8,22 8,34 0,14 -3,26 -7,37 -5,12 -12,22 -20,30 -8,8 -18,15 -30,19 -11,5 -24,7 -37,7 -12,0 -22,-2 -32,-6 -9,-4 -18,-10 -25,-19l0 20 -45 0 0 -88 52 0z"/>
54
+  </g>
55
+ </g>
56
+</svg>
Back to file index

layer.yaml

 1
--- 
 2
+++ layer.yaml
 3
@@ -0,0 +1,19 @@
 4
+"repo": "bzr+ssh://bazaar.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-was-base/trunk/"
 5
+"includes":
 6
+- "layer:basic"
 7
+- "layer:ibm-im"
 8
+- "interface:was-ihs"
 9
+- "interface:db2"
10
+- "interface:ibm-mq"
11
+"options":
12
+  "basic":
13
+    "packages":
14
+    - "unzip"
15
+    - "pwgen"
16
+    "use_venv": !!bool "false"
17
+    "include_system_packages": !!bool "false"
18
+  "silent": !!bool "true"
19
+  "ibm-was-base":
20
+    "silent": !!bool "true"
21
+  "ibm-im": {}
22
+"is": "ibm-was-base"
Back to file index

lib/charms/layer/__init__.py

 1
--- 
 2
+++ lib/charms/layer/__init__.py
 3
@@ -0,0 +1,21 @@
 4
+import os
 5
+
 6
+
 7
+class LayerOptions(dict):
 8
+    def __init__(self, layer_file, section=None):
 9
+        import yaml  # defer, might not be available until bootstrap
10
+        with open(layer_file) as f:
11
+            layer = yaml.safe_load(f.read())
12
+        opts = layer.get('options', {})
13
+        if section and section in opts:
14
+            super(LayerOptions, self).__init__(opts.get(section))
15
+        else:
16
+            super(LayerOptions, self).__init__(opts)
17
+
18
+
19
+def options(section=None, layer_file=None):
20
+    if not layer_file:
21
+        base_dir = os.environ.get('CHARM_DIR', os.getcwd())
22
+        layer_file = os.path.join(base_dir, 'layer.yaml')
23
+
24
+    return LayerOptions(layer_file, section)
Back to file index

lib/charms/layer/basic.py

  1
--- 
  2
+++ lib/charms/layer/basic.py
  3
@@ -0,0 +1,159 @@
  4
+import os
  5
+import sys
  6
+import shutil
  7
+import platform
  8
+from glob import glob
  9
+from subprocess import check_call
 10
+
 11
+from charms.layer.execd import execd_preinstall
 12
+
 13
+
 14
+def bootstrap_charm_deps():
 15
+    """
 16
+    Set up the base charm dependencies so that the reactive system can run.
 17
+    """
 18
+    # execd must happen first, before any attempt to install packages or
 19
+    # access the network, because sites use this hook to do bespoke
 20
+    # configuration and install secrets so the rest of this bootstrap
 21
+    # and the charm itself can actually succeed. This call does nothing
 22
+    # unless the operator has created and populated $CHARM_DIR/exec.d.
 23
+    execd_preinstall()
 24
+    # ensure that $CHARM_DIR/bin is on the path, for helper scripts
 25
+    os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin')
 26
+    venv = os.path.abspath('../.venv')
 27
+    vbin = os.path.join(venv, 'bin')
 28
+    vpip = os.path.join(vbin, 'pip')
 29
+    vpy = os.path.join(vbin, 'python')
 30
+    if os.path.exists('wheelhouse/.bootstrapped'):
 31
+        from charms import layer
 32
+        cfg = layer.options('basic')
 33
+        if cfg.get('use_venv') and '.venv' not in sys.executable:
 34
+            # activate the venv
 35
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 36
+            reload_interpreter(vpy)
 37
+        return
 38
+    # bootstrap wheelhouse
 39
+    if os.path.exists('wheelhouse'):
 40
+        with open('/root/.pydistutils.cfg', 'w') as fp:
 41
+            # make sure that easy_install also only uses the wheelhouse
 42
+            # (see https://github.com/pypa/pip/issues/410)
 43
+            charm_dir = os.environ['CHARM_DIR']
 44
+            fp.writelines([
 45
+                "[easy_install]\n",
 46
+                "allow_hosts = ''\n",
 47
+                "find_links = file://{}/wheelhouse/\n".format(charm_dir),
 48
+            ])
 49
+        apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml'])
 50
+        from charms import layer
 51
+        cfg = layer.options('basic')
 52
+        # include packages defined in layer.yaml
 53
+        apt_install(cfg.get('packages', []))
 54
+        # if we're using a venv, set it up
 55
+        if cfg.get('use_venv'):
 56
+            if not os.path.exists(venv):
 57
+                distname, version, series = platform.linux_distribution()
 58
+                if series in ('precise', 'trusty'):
 59
+                    apt_install(['python-virtualenv'])
 60
+                else:
 61
+                    apt_install(['virtualenv'])
 62
+                cmd = ['virtualenv', '-ppython3', '--never-download', venv]
 63
+                if cfg.get('include_system_packages'):
 64
+                    cmd.append('--system-site-packages')
 65
+                check_call(cmd)
 66
+            os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']])
 67
+            pip = vpip
 68
+        else:
 69
+            pip = 'pip3'
 70
+            # save a copy of system pip to prevent `pip3 install -U pip`
 71
+            # from changing it
 72
+            if os.path.exists('/usr/bin/pip'):
 73
+                shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save')
 74
+        # need newer pip, to fix spurious Double Requirement error:
 75
+        # https://github.com/pypa/pip/issues/56
 76
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse',
 77
+                    'pip'])
 78
+        # install the rest of the wheelhouse deps
 79
+        check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] +
 80
+                   glob('wheelhouse/*'))
 81
+        if not cfg.get('use_venv'):
 82
+            # restore system pip to prevent `pip3 install -U pip`
 83
+            # from changing it
 84
+            if os.path.exists('/usr/bin/pip.save'):
 85
+                shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip')
 86
+                os.remove('/usr/bin/pip.save')
 87
+        os.remove('/root/.pydistutils.cfg')
 88
+        # flag us as having already bootstrapped so we don't do it again
 89
+        open('wheelhouse/.bootstrapped', 'w').close()
 90
+        # Ensure that the newly bootstrapped libs are available.
 91
+        # Note: this only seems to be an issue with namespace packages.
 92
+        # Non-namespace-package libs (e.g., charmhelpers) are available
 93
+        # without having to reload the interpreter. :/
 94
+        reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0])
 95
+
 96
+
 97
+def reload_interpreter(python):
 98
+    """
 99
+    Reload the python interpreter to ensure that all deps are available.
100
+
101
+    Newly installed modules in namespace packages sometimes seemt to
102
+    not be picked up by Python 3.
103
+    """
104
+    os.execle(python, python, sys.argv[0], os.environ)
105
+
106
+
107
+def apt_install(packages):
108
+    """
109
+    Install apt packages.
110
+
111
+    This ensures a consistent set of options that are often missed but
112
+    should really be set.
113
+    """
114
+    if isinstance(packages, (str, bytes)):
115
+        packages = [packages]
116
+
117
+    env = os.environ.copy()
118
+
119
+    if 'DEBIAN_FRONTEND' not in env:
120
+        env['DEBIAN_FRONTEND'] = 'noninteractive'
121
+
122
+    cmd = ['apt-get',
123
+           '--option=Dpkg::Options::=--force-confold',
124
+           '--assume-yes',
125
+           'install']
126
+    check_call(cmd + packages, env=env)
127
+
128
+
129
+def init_config_states():
130
+    import yaml
131
+    from charmhelpers.core import hookenv
132
+    from charms.reactive import set_state
133
+    from charms.reactive import toggle_state
134
+    config = hookenv.config()
135
+    config_defaults = {}
136
+    config_defs = {}
137
+    config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml')
138
+    if os.path.exists(config_yaml):
139
+        with open(config_yaml) as fp:
140
+            config_defs = yaml.safe_load(fp).get('options', {})
141
+            config_defaults = {key: value.get('default')
142
+                               for key, value in config_defs.items()}
143
+    for opt in config_defs.keys():
144
+        if config.changed(opt):
145
+            set_state('config.changed')
146
+            set_state('config.changed.{}'.format(opt))
147
+        toggle_state('config.set.{}'.format(opt), config.get(opt))
148
+        toggle_state('config.default.{}'.format(opt),
149
+                     config.get(opt) == config_defaults[opt])
150
+    hookenv.atexit(clear_config_states)
151
+
152
+
153
+def clear_config_states():
154
+    from charmhelpers.core import hookenv, unitdata
155
+    from charms.reactive import remove_state
156
+    config = hookenv.config()
157
+    remove_state('config.changed')
158
+    for opt in config.keys():
159
+        remove_state('config.changed.{}'.format(opt))
160
+        remove_state('config.set.{}'.format(opt))
161
+        remove_state('config.default.{}'.format(opt))
162
+    unitdata.kv().flush()
Back to file index

lib/charms/layer/execd.py

  1
--- 
  2
+++ lib/charms/layer/execd.py
  3
@@ -0,0 +1,138 @@
  4
+# Copyright 2014-2016 Canonical Limited.
  5
+#
  6
+# This file is part of layer-basic, the reactive base layer for Juju.
  7
+#
  8
+# charm-helpers is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU Lesser General Public License version 3 as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# charm-helpers is distributed in the hope that it will be useful,
 13
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
 14
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15
+# GNU Lesser General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU Lesser General Public License
 18
+# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+# This module may only import from the Python standard library.
 21
+import os
 22
+import sys
 23
+import subprocess
 24
+import time
 25
+
 26
+'''
 27
+execd/preinstall
 28
+
 29
+It is often necessary to configure and reconfigure machines
 30
+after provisioning, but before attempting to run the charm.
 31
+Common examples are specialized network configuration, enabling
 32
+of custom hardware, non-standard disk partitioning and filesystems,
 33
+adding secrets and keys required for using a secured network.
 34
+
 35
+The reactive framework's base layer invokes this mechanism as
 36
+early as possible, before any network access is made or dependencies
 37
+unpacked or non-standard modules imported (including the charms.reactive
 38
+framework itself).
 39
+
 40
+Operators needing to use this functionality may branch a charm and
 41
+create an exec.d directory in it. The exec.d directory in turn contains
 42
+one or more subdirectories, each of which contains an executable called
 43
+charm-pre-install and any other required resources. The charm-pre-install
 44
+executables are run, and if successful, state saved so they will not be
 45
+run again.
 46
+
 47
+    $CHARM_DIR/exec.d/mynamespace/charm-pre-install
 48
+
 49
+An alternative to branching a charm is to compose a new charm that contains
 50
+the exec.d directory, using the original charm as a layer,
 51
+
 52
+A charm author could also abuse this mechanism to modify the charm
 53
+environment in unusual ways, but for most purposes it is saner to use
 54
+charmhelpers.core.hookenv.atstart().
 55
+'''
 56
+
 57
+
 58
+def default_execd_dir():
 59
+    return os.path.join(os.environ['CHARM_DIR'], 'exec.d')
 60
+
 61
+
 62
+def execd_module_paths(execd_dir=None):
 63
+    """Generate a list of full paths to modules within execd_dir."""
 64
+    if not execd_dir:
 65
+        execd_dir = default_execd_dir()
 66
+
 67
+    if not os.path.exists(execd_dir):
 68
+        return
 69
+
 70
+    for subpath in os.listdir(execd_dir):
 71
+        module = os.path.join(execd_dir, subpath)
 72
+        if os.path.isdir(module):
 73
+            yield module
 74
+
 75
+
 76
+def execd_submodule_paths(command, execd_dir=None):
 77
+    """Generate a list of full paths to the specified command within exec_dir.
 78
+    """
 79
+    for module_path in execd_module_paths(execd_dir):
 80
+        path = os.path.join(module_path, command)
 81
+        if os.access(path, os.X_OK) and os.path.isfile(path):
 82
+            yield path
 83
+
 84
+
 85
+def execd_sentinel_path(submodule_path):
 86
+    module_path = os.path.dirname(submodule_path)
 87
+    execd_path = os.path.dirname(module_path)
 88
+    module_name = os.path.basename(module_path)
 89
+    submodule_name = os.path.basename(submodule_path)
 90
+    return os.path.join(execd_path,
 91
+                        '.{}_{}.done'.format(module_name, submodule_name))
 92
+
 93
+
 94
+def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None):
 95
+    """Run command for each module within execd_dir which defines it."""
 96
+    if stderr is None:
 97
+        stderr = sys.stdout
 98
+    for submodule_path in execd_submodule_paths(command, execd_dir):
 99
+        # Only run each execd once. We cannot simply run them in the
100
+        # install hook, as potentially storage hooks are run before that.
101
+        # We cannot rely on them being idempotent.
102
+        sentinel = execd_sentinel_path(submodule_path)
103
+        if os.path.exists(sentinel):
104
+            continue
105
+
106
+        try:
107
+            subprocess.check_call([submodule_path], stderr=stderr,
108
+                                  universal_newlines=True)
109
+            with open(sentinel, 'w') as f:
110
+                f.write('{} ran successfully {}\n'.format(submodule_path,
111
+                                                          time.ctime()))
112
+                f.write('Removing this file will cause it to be run again\n')
113
+        except subprocess.CalledProcessError as e:
114
+            # Logs get the details. We can't use juju-log, as the
115
+            # output may be substantial and exceed command line
116
+            # length limits.
117
+            print("ERROR ({}) running {}".format(e.returncode, e.cmd),
118
+                  file=stderr)
119
+            print("STDOUT<<EOM", file=stderr)
120
+            print(e.output, file=stderr)
121
+            print("EOM", file=stderr)
122
+
123
+            # Unit workload status gets a shorter fail message.
124
+            short_path = os.path.relpath(submodule_path)
125
+            block_msg = "Error ({}) running {}".format(e.returncode,
126
+                                                       short_path)
127
+            try:
128
+                subprocess.check_call(['status-set', 'blocked', block_msg],
129
+                                      universal_newlines=True)
130
+                if stop_on_error:
131
+                    sys.exit(0)  # Leave unit in blocked state.
132
+            except Exception:
133
+                pass  # We care about the exec.d/* failure, not status-set.
134
+
135
+            if stop_on_error:
136
+                sys.exit(e.returncode or 1)  # Error state for pre-1.24 Juju
137
+
138
+
139
+def execd_preinstall(execd_dir=None):
140
+    """Run charm-pre-install for each module within execd_dir."""
141
+    execd_run('charm-pre-install', execd_dir=execd_dir)
Back to file index

metadata.yaml

 1
--- 
 2
+++ metadata.yaml
 3
@@ -0,0 +1,52 @@
 4
+"name": "ibm-was-base"
 5
+"summary": "WebSphere Application Server Base Edition"
 6
+"maintainer": "IBM Juju Support Team <jujusupp@us.ibm.com>"
 7
+"description": |
 8
+  WebSphere Application Server is a proven, high-performance transaction engine that can help build, run, integrate, and manage dynamic web applications.
 9
+"tags":
10
+- "ibm"
11
+- "im"
12
+- "ibm-z"
13
+- "misc"
14
+- "ibm"
15
+"requires":
16
+  "wasdb":
17
+    "interface": "db2"
18
+  "wasmessaging":
19
+    "interface": "ibm-mq"
20
+"provides":
21
+  "was-ihs":
22
+    "interface": "was-ihs"
23
+"resources":
24
+  "ibm_im_installer":
25
+    "type": "file"
26
+    "filename": "ibm_im_installer.zip"
27
+    "description": "Installation Manager installer archive"
28
+  "ibm_im_fixpack":
29
+    "type": "file"
30
+    "filename": "ibm_im_fixpack.zip"
31
+    "description": "Installation Manager fix pack archive"
32
+  "ibm_was_base_installer":
33
+    "type": "file"
34
+    "filename": "ibm_was_base_installer.zip"
35
+    "description": "WAS Base installer archive"
36
+  "ibm_java_sdk_installer":
37
+    "type": "file"
38
+    "filename": "ibm_java_sdk.zip"
39
+    "description": "IBM Java SDK installer archive"
40
+  "ibm_was_base_fp":
41
+    "type": "file"
42
+    "filename": "ibm_was_base_fp.zip"
43
+    "description": "WAS Base fix pack archive"
44
+  "ibm_java_sdk_fp":
45
+    "type": "file"
46
+    "filename": "ibm_java_sdk_fp.zip"
47
+    "description": "IBM Java SDK fix pack archive"
48
+"series":
49
+- "xenial"
50
+- "trusty"
51
+"subordinate": !!bool "false"
52
+"terms":
53
+- "ibmcharmers/ibm-im/1"
54
+- "ibm-was-base/3"
55
+- "ibm-java-sdk/1"
Back to file index

reactive/ibm-im.sh

  1
--- 
  2
+++ reactive/ibm-im.sh
  3
@@ -0,0 +1,187 @@
  4
+#!/bin/bash
  5
+set -e
  6
+
  7
+source charms.reactive.sh
  8
+ARCHITECTURE=`uname -m`
  9
+
 10
+# Installation Manager install path
 11
+IM_INSTALL_PATH="/opt/IBM/InstallationManager"
 12
+
 13
+@when_not 'ibm-im.installed'
 14
+function install_ibm_im(){
 15
+      # Fail fast if we're on an unsupported arch
 16
+      if [ "$ARCHITECTURE" != "x86_64" -a  "$ARCHITECTURE" != "ppc64le" -a "$ARCHITECTURE" != "s390x" ]; then
 17
+            juju-log "IBM IM: only supported on x86_64 or ppc64le or s390x platforms"
 18
+            status-set blocked "unsupported architecture"
 19
+            return 1
 20
+      fi
 21
+
 22
+      # Get the installable resource
 23
+      juju-log "IBM IM: fetching the ibm_im_installer resource"
 24
+      status-set maintenance "fetching the ibm_im_installer resource"
 25
+      cfg_im_pkg_name=`resource-get 'ibm_im_installer' || echo unavailable`
 26
+
 27
+      # If we don't have a package, report blocked status; we can't proceed.
 28
+      if [ "$cfg_im_pkg_name" = "unavailable" ]; then
 29
+        juju-log "IBM IM: missing required ibm_im_installer resource"
 30
+        status-set blocked "missing required ibm_im_installer resource"
 31
+        return 0
 32
+      fi
 33
+
 34
+      juju-log "IBM IM: using $cfg_im_pkg_name as the ibm_im_installer resource"
 35
+      ARCHIVE_DIR=`dirname $cfg_im_pkg_name`
 36
+
 37
+      # Extract the installer contents
 38
+      if [ -f  $cfg_im_pkg_name ]; then
 39
+           juju-log "IBM IM: extracting the ibm_im_installer resource"
 40
+           status-set maintenance "extracting the ibm_im_installer resource"
 41
+           if [ -f $ARCHIVE_DIR/userinstc ]; then
 42
+                 juju-log "IBM IM: ibm_im_installer resource already extracted"
 43
+           else
 44
+                 cd $ARCHIVE_DIR
 45
+                 if ! unzip $cfg_im_pkg_name; then
 46
+                      juju-log "IBM IM: Unable to extract the ibm_im_installer resource"
 47
+                      # Remove corrupt archive file
 48
+                      status-set blocked "ibm_im_installer resource is corrupt"
 49
+                      rm -rf $ARCHIVE_DIR
 50
+                      return 0
 51
+                 else
 52
+                      juju-log "IBM IM: ibm_im_installer resource extracted successfully"
 53
+                 fi
 54
+           fi
 55
+      fi
 56
+
 57
+      # Do the actual IBM IM install
 58
+      if [ -f  $ARCHIVE_DIR/userinstc ]; then
 59
+        juju-log "IBM IM: starting installation."
 60
+        status-set maintenance "installing ibm-im"
 61
+        cp $ARCHIVE_DIR/install.xml $ARCHIVE_DIR/silent_install.xml
 62
+        sed -i "2 a \<profile kind='self' installLocation='$IM_INSTALL_PATH' id='IBM Installation Manager'>\n <data key='eclipseLocation' value='$IM_INSTALL_PATH' />\n</profile>" $ARCHIVE_DIR/silent_install.xml
 63
+        if $ARCHIVE_DIR/userinstc -input $ARCHIVE_DIR/silent_install.xml -acceptlicense; then
 64
+            set_state 'ibm-im.installed'
 65
+            status-set active "ready"
 66
+        else
 67
+            juju-log "IBM IM: error while installing"
 68
+            return 1
 69
+        fi
 70
+      else
 71
+        juju-log "IBM IM: installer was not found."
 72
+        status-set blocked "installation failed"
 73
+        return 1
 74
+      fi
 75
+}
 76
+
 77
+@when 'ibm-im.installed'
 78
+@when_not 'ibm-im.updated'
 79
+function install_ibm_im_fixpack(){
 80
+    # Get the fixpack resource
 81
+    juju-log "IBM IM: fetching the ibm_im_fixpack resource"
 82
+    status-set maintenance "fetching the ibm_im_fixpack resource"
 83
+    ibm_im_fp_package=`resource-get 'ibm_im_fixpack' || echo unavailable`
 84
+
 85
+    # If we don't have a fixpack, just exit successfully; there's nothing to do.
 86
+    if [ "$ibm_im_fp_package" = "unavailable" ]; then
 87
+      juju-log "IBM IM: no ibm_im_fixpack to install"
 88
+      status-set active "ready"
 89
+      return 0
 90
+    fi
 91
+
 92
+    # Currently, there is no way to make a resource optional, so something
 93
+    # must be uploaded to the charm store, even if it's a dummy archive.
 94
+    # If we detect the fixpack is just a placeholder (because we created an
 95
+    # empty placeholder in the store), exit just like we do with a missing
 96
+    # fixpack resource.
 97
+    ibm_im_fp_empty=`file $ibm_im_fp_package | { grep -q empty && echo "True"; } || echo "False"`
 98
+    if [ "$ibm_im_fp_empty" = "True" ]; then
 99
+      juju-log "IBM IM: no ibm_im_fixpack to install"
100
+      status-set active "ready"
101
+      return 0
102
+    fi
103
+
104
+    juju-log "IBM IM: using $ibm_im_fp_package as the ibm_im_fixpack resource"
105
+    ARCHIVE_DIR=`dirname $ibm_im_fp_package`
106
+
107
+    # Extract the fixpack contents
108
+    if [ -f $ibm_im_fp_package ]; then
109
+        juju-log "IBM IM: extracting the ibm_im_fixpack resource"
110
+        status-set maintenance "extracting the ibm_im_fixpack resource"
111
+        if [ -f $ARCHIVE_DIR/userinstc ]; then
112
+              juju-log "IBM IM: ibm_im_fixpack resource already extracted"
113
+        else
114
+              cd $ARCHIVE_DIR
115
+              if ! unzip $ibm_im_fp_package; then
116
+                      juju-log "IBM IM: Unable to extract the ibm_im_fixpack resource"
117
+                      # Remove corrupt archive file
118
+                      status-set blocked "ibm_im_fixpack resource is corrupt"
119
+                      rm  -rf $ARCHIVE_DIR
120
+                      return 0
121
+              else
122
+                      juju-log "IBM IM: ibm_im_fixpack resource extracted successfully"
123
+              fi
124
+        fi
125
+    fi
126
+
127
+    # Do the actual IBM IM fixpack install
128
+    if [ -f $ARCHIVE_DIR/userinstc ]; then
129
+        juju-log "IBM IM: starting fixpack installation."
130
+        status-set maintenance "installing fixpack"
131
+        cp $ARCHIVE_DIR/install.xml $ARCHIVE_DIR/silent_install.xml
132
+        sed -i "2 a \<profile kind='self' installLocation='$IM_INSTALL_PATH' id='IBM Installation Manager'>\n <data key='eclipseLocation' value='$IM_INSTALL_PATH' />\n</profile>" $ARCHIVE_DIR/silent_install.xml
133
+        if $ARCHIVE_DIR/userinstc -input $ARCHIVE_DIR/silent_install.xml -acceptlicense; then
134
+             set_state 'ibm-im.updated'
135
+             status-set active "ready"
136
+        else
137
+             juju-log "IBM IM: error while installing the fixpack."
138
+             return 1
139
+        fi
140
+    else
141
+        juju-log "IBM_IM: Fix pack installer was not found."
142
+        status-set blocked "fixpack installation failed"
143
+        return 1
144
+   fi
145
+}
146
+
147
+@hook 'upgrade-charm'
148
+function check_fixpack(){
149
+    # The upgrade-charm hook will fire when a new resource is pushed for this
150
+    # charm. This is a good time to determine if we need to deal with a new
151
+    # fixpack.
152
+    if ! charms.reactive is_state 'ibm-im.updated'; then
153
+        # If there is no prior fixpack installed (because ibm-im.updated is not
154
+        # set), do nothing since install-ibm-im-fixpack will handle that case.
155
+        juju-log "IBM IM: no fixpack has been installed; nothing to upgrade."
156
+        return 0
157
+    else
158
+        # If we have a fixpack already (because ibm-im.updated is set),
159
+        # we should fetch the latest fixpack and determine if it is new.
160
+        #  - If it is new, set our states so install-ibm-im-fixpack is called again
161
+        #  - If it is not new, do nothing
162
+        juju-log "IBM IM: scanning for new fixpacks to install"
163
+        ARCHIVE_DIR="$CHARM_DIR/../resources/ibm_im_fixpack"
164
+        CUR_FIXPACK="$ARCHIVE_DIR/ibm_im_fixpack.zip"
165
+
166
+        # Sum any existing fixpack to determine if we have a new one
167
+        if [ -f $CUR_FIXPACK ]; then
168
+            CUR_MD5=`md5sum "$CUR_FIXPACK" | awk '{print $1}'`
169
+            # Calling resource-get here will fetch the fixpack resource.
170
+            NEW_FIXPACK=`resource-get 'ibm_im_fixpack' || echo unavailable`
171
+            if [ "$NEW_FIXPACK" = "unavailable" ]; then
172
+                juju-log "IBM IM: no fixpack to install"
173
+            else
174
+                NEW_MD5=`md5sum "$NEW_FIXPACK" | awk '{print $1}'`
175
+                # If sums don't match, we have a new fp. Configure states so
176
+                # we re-run install-ibm-im-fixpack().
177
+                if [ "$CUR_MD5" != "$NEW_MD5" ]; then
178
+                    juju-log "IBM IM: new fixpack detected ($CUR_FIXPACK with $CUR_MD5 versus $NEW_FIXPACK with $NEW_MD5)"
179
+                    rm -rf $ARCHIVE_DIR
180
+                    remove_state 'ibm-im.updated'
181
+                else
182
+                    juju-log "IBM IM: no new fixpack to install"
183
+                fi
184
+            fi
185
+        fi
186
+    fi
187
+}
188
+
189
+
190
+reactive_handler_main
Back to file index

reactive/ibm-was-base.sh

  1
--- 
  2
+++ reactive/ibm-was-base.sh
  3
@@ -0,0 +1,624 @@
  4
+#!/bin/bash
  5
+set -ex
  6
+
  7
+source charms.reactive.sh
  8
+
  9
+reactive_states=`charms.reactive -y get_states`
 10
+
 11
+WAS_BASE_INSTALL_PATH=/root/IBM/WebSphere/AppServer/V90/BASE
 12
+IM_INSTALL_PATH=/opt/IBM/InstallationManager
 13
+ARCHITECTURE=`uname -m`
 14
+profile_name=`config-get profile_name`
 15
+was_admin_name=`config-get was_admin_user`
 16
+old_user_name=""
 17
+old_password=""
 18
+old_profile_path=""
 19
+was_admin_pw=`config-get was_admin_pw`
 20
+profilepath=`config-get profile_path`
 21
+profile_template_path="$WAS_BASE_INSTALL_PATH/profileTemplates/default"
 22
+ihsadminuser="usr"
 23
+ihsadminpw=`pwgen -N 1 15`
 24
+ihsadmingrp="grp"
 25
+
 26
+if [ "$ARCHITECTURE" != "s390x" -a "$ARCHITECTURE" != "x86_64" -a "$ARCHITECTURE" != "ppc64le" ]; then
 27
+	juju-log "IBM WAS Base: Unsupported platform. IBM WAS Base installed with this Charm supports only the x86_64, ppc64le and s390x platform."
 28
+	status-set blocked "IBM WAS Base: Unsupported architecture"
 29
+	exit 1
 30
+fi
 31
+
 32
+	
 33
+if [ -f $CHARM_DIR/files/was_users.txt ]; then
 34
+	source $CHARM_DIR/files/was_users.txt
 35
+	old_user_name=$user_name
 36
+	old_password=$password
 37
+	old_profile_path=$profile_path
 38
+fi
 39
+
 40
+#Get the cfguser name based on the remote unit name
 41
+get_cfgusername()
 42
+{
 43
+	cfgusername=$1
 44
+	cfgusername=`echo $cfgusername | cut -d"/" -f1`
 45
+	cfgusername=`echo "$cfgusername" | sed -r 's/-//g' | sed -r 's/ibm//g'`
 46
+	cfgusername=`echo "$cfgusername" | awk '{print substr($0,0,5)}'`
 47
+	ihsadminuser="$cfgusername$ihsadminuser"
 48
+	ihsadmingrp="$cfgusername$ihsadmingrp"
 49
+
 50
+}
 51
+
 52
+stop_was_base_server() {
 53
+	if [ "$1" != "" -a "$1" != $was_admin_name ]; then
 54
+		was_admin_name=$1
 55
+	fi
 56
+	if [ "$2" != "" -a "$2" != $was_admin_pw ]; then
 57
+		was_admin_pw=$2
 58
+	fi
 59
+	if [ "$3" != "" -a "$3" != "$profilepath" ]; then
 60
+		profilepath=$3
 61
+	fi
 62
+	profile_name=`ls $profilepath`
 63
+	profile_name=`basename $profile_name`
 64
+	juju-log "IBM WAS Base: profile_nmae===="$profile_name"==was_admin_name==="$was_admin_name"====was_admin_pw=="$was_admin_pw"profilepath====$profilepath"
 65
+	if [ -d "$profilepath/$profile_name/bin" ]; then
 66
+		cd $profilepath/$profile_name/bin/
 67
+		if ! ./stopServer.sh server1 -username $was_admin_name -password $was_admin_pw
 68
+		then
 69
+			status-set blocked "IBM WAS Base:Error while stoping the server"
 70
+			return 0
 71
+		else
 72
+			status-set active "IBM WAS Base:Server stopped successfully"
 73
+			close-port 9060
 74
+			close-port 9043
 75
+			remove_state 'ibm-was-base.server.started'
 76
+		fi
 77
+	else
 78
+		juju-log "IBM WAS Base: No server to stop, exiting."
 79
+		return 0
 80
+	fi
 81
+}
 82
+
 83
+install_was_base_fixpack() {
 84
+	if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.BASE.v90 -acceptLicense -repositories $CHARM_DIR/../resources/WAS_BASE/FP -installationDirectory $WAS_BASE_INSTALL_PATH  -showProgress
 85
+	then
 86
+		set_state 'ibm-was-base.updated'
 87
+		status-set active "IBM WAS Base: WAS Base installation upgraded successfully!"
 88
+	else
 89
+		status-set blocked "IBM WAS Base: Error while upgrading WAS Base!"
 90
+		return 1
 91
+	fi
 92
+
 93
+}
 94
+
 95
+install_java_sdk_fixpack() {
 96
+	if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.java.jdk.v8 -acceptLicense -repositories $CHARM_DIR/../resources/IBM_SDK/FP -installationDirectory $WAS_BASE_INSTALL_PATH  -showProgress
 97
+	then
 98
+		set_state 'ibm-was-base.java.updated'
 99
+		status-set active "IBM WAS Base: WAS Base installation upgraded successfully!"
100
+	else
101
+		status-set blocked "IBM WAS Base: Error while upgrading WAS Base!"
102
+		return 1
103
+	fi
104
+
105
+}
106
+
107
+@when 'ibm-im.installed'
108
+@when_not 'ibm-was-base.installed'
109
+function install_was_base() {
110
+	juju-log "IBM WAS Base: Checking etc/hosts file."
111
+	private_address=`unit-get private-address`
112
+	juju-log "IBM WAS Base:private_address : $private_address"
113
+	cd /etc
114
+	if grep -q "$private_address" hosts
115
+	then
116
+		juju-log "IBM WAS Base: Host file already updated"
117
+	else
118
+		juju-log "IBM WAS Base: Updating Host file"
119
+		echo "$private_address `hostname`" >> /etc/hosts
120
+	fi
121
+
122
+	# Get the installable WAS Base resources
123
+	juju-log "IBM WAS Base: fetching the ibm_was_base_installer resource"
124
+	status-set maintenance "fetching the ibm_was_base_installer resource"
125
+	cfg_was_base_pkg_name=`resource-get 'ibm_was_base_installer' || echo unavailable`
126
+	
127
+	juju-log "IBM WAS Base: fetching the ibm_java_sdk resource"
128
+	status-set maintenance "fetching the ibm_java_sdk resource"
129
+	ibm_java_sdk_pkg_name=`resource-get 'ibm_java_sdk_installer' || echo unavailable`
130
+
131
+	ibm_was_pkg_isempty=`file $cfg_was_base_pkg_name | { grep -q empty && echo "True"; } || echo "False"`
132
+	ibm_java_sdk_pkg_isempty=`file $ibm_java_sdk_pkg_name | { grep -q empty && echo "True"; } || echo "False"`
133
+		
134
+	# If we don't have a package, report blocked status; we can't proceed.
135
+	if [ "$cfg_was_base_pkg_name" = "unavailable" -o "$ibm_java_sdk_pkg_name" = "unavailable" ]; then
136
+		juju-log "IBM WAS Base: missing required ibm_was_base_installer resource or ibm_java_sdk_installer resource"
137
+		status-set blocked "missing required ibm_was_base_installer resource or ibm_java_sdk_installer resource"
138
+		return 0
139
+	fi
140
+		
141
+	if [ "$ibm_was_pkg_isempty" = "True" -o "$ibm_java_sdk_pkg_isempty" = "True" ]; then
142
+		juju-log "IBM WAS Base: missing required ibm_was_base_installer resource or ibm_java_sdk_installer resource, found empty packages"
143
+		status-set blocked "IBM WAS Base:empty resource found, Please provide ibm_was_base_installer resource and ibm_java_sdk_installer resources."
144
+		return 0
145
+	fi
146
+	
147
+	juju-log "IBM WAS Base: using $cfg_was_base_pkg_name $ibm_java_sdk_pkg_name as the resources for this charm."
148
+	was_base_installer="$cfg_was_base_pkg_name" 
149
+	ibm_java_sdk_resources="$ibm_java_sdk_pkg_name"
150
+	
151
+	if [ -f $was_base_installer ]; then
152
+		ARCHIVE_DIR=`dirname $was_base_installer`
153
+		juju-log "IBM WAS Base: extracting the ibm_was_base_installer resource"
154
+		status-set maintenance "extracting the ibm_was_base_installer resource"
155
+		if [ -d $ARCHIVE_DIR/../WAS_BASE -a "$(ls -A $ARCHIVE_DIR/../WAS_BASE)" ]; then
156
+			juju-log "IBM WAS Base: ibm_was_base_installer resources already extracted"
157
+		else
158
+			if [ ! -d $ARCHIVE_DIR/../WAS_BASE ]; then
159
+				mkdir $ARCHIVE_DIR/../WAS_BASE
160
+			fi
161
+			if ! unzip $was_base_installer -d $ARCHIVE_DIR/../WAS_BASE
162
+			then
163
+				juju-log "IBM WAS Base: Unable to extract the WAS Base packages content. Verify whether the package is corrupt."
164
+				# Remove corrupt archive file
165
+				status-set blocked "IBM WAS Base packages are corrupt"
166
+				rm -rf $ARCHIVE_DIR/../WAS_BASE
167
+				return 0
168
+			else
169
+				juju-log "IBM WAS Base: $was_pkg extracted successfully"
170
+			fi
171
+		fi
172
+	fi
173
+
174
+	if [ -f $ibm_java_sdk_resources ]; then
175
+		ARCHIVE_DIR=`dirname $ibm_java_sdk_resources`
176
+		juju-log "IBM WAS Base: extracting the ibm_java_sdk_installer resource"
177
+		status-set maintenance "extracting the ibm_java_sdk_installer resource"
178
+		if [ -d $ARCHIVE_DIR/../IBM_SDK -a "$(ls -A $ARCHIVE_DIR/../IBM_SDK)" ];then
179
+			juju-log "IBM WAS Base: ibm_java_sdk_installer resource already extracted"
180
+		else
181
+			if [ ! -d $ARCHIVE_DIR/../IBM_SDK ]; then
182
+				mkdir $ARCHIVE_DIR/../IBM_SDK
183
+			fi				
184
+			if ! unzip $ibm_java_sdk_resources -d $ARCHIVE_DIR/../IBM_SDK
185
+			then
186
+				juju-log "IBM WAS Base: Unable to extract the IBM Java SDK packages content. Verify whether the package is corrupt."
187
+				# Remove corrupt archive file
188
+				status-set blocked "IBM Java SDK packages are corrupt"
189
+				rm -rf $ARCHIVE_DIR/../IBM_SDK
190
+				return 0
191
+			else
192
+				juju-log "IBM WAS Base: $sdk_pkg extracted successfully"
193
+			fi
194
+		fi
195
+	fi
196
+
197
+	
198
+	juju-log "IBM WAS Base: starting was base installation."
199
+	status-set maintenance "IBM WAS Base: Installing WAS BASE"
200
+	#sh does not work in ubuntu so linking to bash
201
+	juju-log "IBM WAS Base: Unlink /bin/sh"
202
+	unlink /bin/sh
203
+	ln -s /bin/bash /bin/sh
204
+	if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.BASE.v90, com.ibm.java.jdk.v8 -acceptLicense -repositories $ARCHIVE_DIR/../WAS_BASE,$ARCHIVE_DIR/../IBM_SDK -installationDirectory $WAS_BASE_INSTALL_PATH  -showProgress
205
+	then
206
+		set_state 'ibm-was-base.installed'
207
+		status-set active "IBM WAS Base: WAS Base is installed successfully!"
208
+	else
209
+		status-set blocked "IBM WAS Base: WAS Base Installation failed!"
210
+		return 1
211
+	fi
212
+}
213
+
214
+@when 'ibm-was-base.installed'
215
+@when_not_all 'ibm-was-base.updated' 'ibm-was-base.java.updated'
216
+function update_was_base(){
217
+	# Get the WAS Base fixpack resources
218
+	juju-log "IBM WAS Base: fetching the ibm_was_base_fp resource"
219
+	status-set maintenance "fetching the ibm_was_base_fp resource"
220
+	was_base_fp=`resource-get 'ibm_was_base_fp' || echo unavailable`
221
+		
222
+	juju-log "IBM WAS Base: fetching the ibm_java_sdk_fp resource"
223
+	status-set maintenance "fetching the ibm_java_sdk_fp resource"
224
+	java_sdk_fp=`resource-get 'ibm_java_sdk_fp' || echo unavailable`
225
+	
226
+	#If we don't have a fixpack, just exit successfully; there's nothing to do.
227
+	if [ "$was_base_fp" = "unavailable" -a "$java_sdk_fp" = "unavailable" ]; then
228
+		juju-log "IBM WAS Base: no ibm_was_base_fixpack packages to install"
229
+		status-set active "IBM WAS Base is ready"
230
+		return 0
231
+	fi
232
+	
233
+	#If we detect the fixpack is just a placeholder in charm store, just exit successfully; there's nothing to do.
234
+	ibm_was_fp_isempty=`file $was_base_fp | { grep -q empty && echo "True"; } || echo "False"`
235
+	ibm_java_sdk_fp_isempty=`file $java_sdk_fp | { grep -q empty && echo "True"; } || echo "False"`
236
+	if [ "$ibm_was_fp_isempty" = "True" -a "$ibm_java_sdk_fp_isempty" = "True" ]; then
237
+		juju-log "IBM WAS Base: no ibm_was_base_fixpack packages to install"
238
+		status-set active "IBM WAS Base is ready"
239
+		return 0
240
+	fi
241
+	
242
+	if ! charms.reactive is_state 'ibm-was-base.updated'
243
+	then 
244
+		if [ -f "$was_base_fp"  ]
245
+		then
246
+			was_base_fp_installer="$was_base_fp"
247
+			if [ -f $was_base_fp_installer ]; then
248
+				ARCHIVE_DIR=`dirname $was_base_fp_installer`
249
+				juju-log "IBM WAS Base: extracting the ibm_was_base_fp_installer resource"
250
+				status-set maintenance "extracting the ibm_was_base_fp_installer resource"
251
+				if [ -d $ARCHIVE_DIR/../WAS_BASE/FP -a "$(ls -A $ARCHIVE_DIR/../WAS_BASE/FP)" ]; then
252
+					juju-log "IBM WAS Base: ibm_was_base_fp_installer resources already extracted"
253
+				else
254
+					if [ ! -d $ARCHIVE_DIR/../WAS_BASE/FP ]; then
255
+						mkdir $ARCHIVE_DIR/../WAS_BASE/FP
256
+					fi
257
+					if ! unzip $was_base_fp_installer -d $ARCHIVE_DIR/../WAS_BASE/FP
258
+					then
259
+						juju-log "IBM WAS Base: Unable to extract the WAS Base packages content. Verify whether the package is corrupt."
260
+						# Remove corrupt archive file
261
+						status-set blocked "IBM WAS Base packages are corrupt"
262
+						rm -rf $ARCHIVE_DIR/../WAS_BASE/FP
263
+						return 0
264
+					else
265
+						juju-log "IBM WAS Base: $was_base_fp extracted successfully"
266
+					fi
267
+				fi
268
+			fi
269
+			if charms.reactive is_state 'ibm-was-base.server.started'; then
270
+			stop_was_base_server $old_user_name $old_password $old_profile_path
271
+			fi
272
+			install_was_base_fixpack
273
+		fi
274
+	fi
275
+	if ! charms.reactive is_state 'ibm-was-base.java.updated'
276
+	then 
277
+		if [ -f "$java_sdk_fp" ]
278
+		then
279
+			ibm_java_sdk_fp_resource="$java_sdk_fp"
280
+			if [ -f $ibm_java_sdk_fp_resource ]; then
281
+				ARCHIVE_DIR=`dirname $ibm_java_sdk_fp_resource`
282
+				juju-log "IBM WAS Base: extracting the ibm_java_sdk_fp_installer resources"
283
+				status-set maintenance "extracting the ibm_java_sdk_fp_installer resources"
284
+				if [ -d $ARCHIVE_DIR/../IBM_SDK/FP -a "$(ls -A $ARCHIVE_DIR/../IBM_SDK/FP)" ];then
285
+					juju-log "IBM WAS Base: ibm_java_sdk_fp_installer resources already extracted"
286
+				else
287
+					if [ ! -d $ARCHIVE_DIR/../IBM_SDK/FP ]; then
288
+						mkdir $ARCHIVE_DIR/../IBM_SDK/FP
289
+					fi				
290
+					if ! unzip $ibm_java_sdk_fp_resource -d $ARCHIVE_DIR/../IBM_SDK/FP
291
+					then
292
+						juju-log "IBM WAS Base: Unable to extract the IBM Java SDK packages content. Verify whether the package is corrupt."
293
+						# Remove corrupt archive file
294
+						status-set blocked "IBM Java SDK packages are corrupt"
295
+						rm -rf $ARCHIVE_DIR/../IBM_SDK/FP
296
+						return 0
297
+					else
298
+						juju-log "IBM WAS Base: $sdk_fp_pkg extracted successfully"
299
+					fi
300
+				fi
301
+			fi
302
+			if charms.reactive is_state 'ibm-was-base.server.started'; then
303
+				stop_was_base_server $old_user_name $old_password $old_profile_path
304
+			fi
305
+			install_java_sdk_fixpack
306
+		fi
307
+	fi
308
+}
309
+
310
+
311
+@when 'ibm-was-base.installed'
312
+@when_not 'ibm-was-base.profile.available'
313
+function create_profile(){
314
+	$WAS_BASE_INSTALL_PATH/bin/manageprofiles.sh -create -profileName $profile_name -profilePath $profilepath/$profile_name -templatePath $profile_template_path -enableAdminSecurity true -serverName "server1" -adminUserName $was_admin_name -adminPassword $was_admin_pw
315
+	set_state 'ibm-was-base.profile.available'
316
+	if [ ! -d $CHARM_DIR/files ]; then
317
+		mkdir $CHARM_DIR/files
318
+	fi
319
+	if [ -f $CHARM_DIR/files/was_users.txt ]; then
320
+		juju-log "IBM WAS Base: was_users.txt file already exists."
321
+	else
322
+		touch $CHARM_DIR/files/was_users.txt
323
+	fi
324
+	
325
+cat > $CHARM_DIR/files/was_users.txt <<EOF
326
+user_name=$was_admin_name
327
+password=$was_admin_pw
328
+profile_path=$profilepath
329
+EOF
330
+	chmod 600 $CHARM_DIR/files/was_users.txt
331
+}
332
+
333
+@when 'ibm-was-base.installed' 'ibm-was-base.profile.available'
334
+@when_not 'ibm-was-base.server.started'
335
+function start_was_base_server() {
336
+	if [ "$old_profile_path" != "" ]; then
337
+		profilepath=$old_profile_path
338
+		profile_name=`ls $old_profile_path`
339
+		profile_name=`basename $profile_name`
340
+	fi
341
+	if [ -d $profilepath/$profile_name/bin ]; then
342
+		cd $profilepath/$profile_name/bin/
343
+		if ! ./startServer.sh server1
344
+		then
345
+			status-set blocked "IBM WAS Base: Error while starting the server"
346
+		else
347
+			status-set active "IBM WAS Base: Server started successfully"
348
+			open-port 9060
349
+			open-port 9043
350
+			set_state 'ibm-was-base.server.started'
351
+		fi
352
+	else
353
+		status-set blocked "IBM WAS Base: No server to start, exiting."
354
+		return 0
355
+	fi
356
+}
357
+
358
+
359
+@when_not_all 'config.default.profile_name' 'config.default.was_admin_user' 'config.default.was_admin_pw' 'config.default.profile_path'
360
+@when_any 'config.changed.profile_name' 'config.changed.was_admin_user' 'config.changed.was_admin_pw' 'config.changed.profile_path'
361
+@when_not 'was-ihs.available'
362
+function create_new_profile() {
363
+	
364
+	if  charms.reactive is_state 'was-ihs.available'; then
365
+		status-set blocked "IBM WAS Base: Please remove the relation before creating new profile."
366
+		return 0
367
+	fi
368
+	if [ "$was_admin_name" =="" -o  "$was_admin_pw" == "" -o "$profilepath" == "" -o "$profile_name" == "" ]; then
369
+		status-set blocked "IBM WAS Base: Please provide the valid values for config options."
370
+		return 0
371
+	fi
372
+		
373
+	juju-log "IBM WAS Base: old_was_admin_user=$old_user_name, old_was_admin_password=$old_password, old_profile_path=$old_profile_path"
374
+	if [ -d $profilepath -o "$old_profile_path" != "" -a "$old_profile_path" != "$profilepath" ]; then
375
+		if [ "$old_profile_path" != "" -a "$old_profile_path" != "$profilepath" ]; then
376
+			juju-log "IBM WAS Base: old_profile_path===$old_profile_path profilepath=$profilepath"
377
+			old_profile_name=`ls $old_profile_path`
378
+		else
379
+			juju-log "IBM WAS Base: old_profile_path===$old_profile_path profilepath=$profilepath"
380
+			old_profile_name=`ls $profilepath`
381
+		fi
382
+		old_profile_name=`basename $old_profile_name`
383
+		stop_was_base_server $old_user_name $old_password $old_profile_path
384
+		cd /
385
+		$WAS_BASE_INSTALL_PATH/bin/manageprofiles.sh -delete -profileName $old_profile_name
386
+		if [ -d "$old_profile_path" ]; then
387
+			rm -rf $old_profile_path
388
+		else
389
+			juju-log "IBM WAS Base: profilepath to be deleted $profilepath"
390
+			rm -rf $profilepath
391
+		fi
392
+		remove_state 'ibm-was-base.profile.available'
393
+		juju-log "IBM WAS Base: Removed old profile."
394
+	fi
395
+					
396
+}
397
+
398
+@hook 'upgrade-charm'
399
+function check_fixpack(){
400
+	# The upgrade-charm hook will fire when a new resource is pushed for this
401
+	# charm. This is a good time to determine if we need to deal with a new
402
+	# fixpack.
403
+	if ! charms.reactive is_state 'ibm-was-base.updated'; then
404
+		# If there is no prior fixpack installed (because ibm-was-base.updated is not
405
+		# set), do nothing since update_was_base_fixpack will handle that case.
406
+		juju-log "IBM WAS Base: no fixpack has been installed; nothing to upgrade."
407
+		return 0
408
+	else
409
+		# If we have a fixpack packges already (because ibm-was-base.updated is set),
410
+		# we should fetch the latest fixpack packages and determine if it is new.
411
+		#  - If it is new, set our states so install_was_base_fixpack is called again
412
+		#  - If it is not new, do nothing
413
+		juju-log "IBM WAS Base: scanning for new fixpacks to install"
414
+		WAS_FP_DIR="$CHARM_DIR/../resources/ibm_was_base_fp"
415
+
416
+		CUR_WAS_FP="$WAS_FP_DIR/ibm_was_base_fp.zip"
417
+		if [ -f $CUR_WAS_FP ]; then
418
+			CUR_WAS_FP_MD5=`md5sum "$CUR_WAS_FP" | awk '{print $1}'`
419
+		fi
420
+		
421
+		NEW_WAS_FP=`resource-get 'ibm_was_base_fp' || echo unavailable`
422
+		
423
+		if [ "$NEW_WAS_FP" = "unavailable" ]; then
424
+			juju-log "IBM WAS Base: no fixpacks to install"
425
+		elif [ -f $NEW_WAS_FP ]; then
426
+			NEW_WAS_FP_MD5=`md5sum "$NEW_WAS_FP" | awk '{print $1}'`
427
+		fi
428
+		
429
+		if [ "$CUR_WAS_FP_MD5" != "$NEW_WAS_FP_MD5" ]; then
430
+			juju-log "IBM WAS Base: new fixpack detected ($CUR_WAS_FP with $CUR_WAS_FP_MD5 versus $NEW_WAS_FP with $NEW_WAS_FP_MD5)"
431
+			rm -rf $CHARM_DIR/../resources/WAS_BASE/FP
432
+			remove_state 'ibm-was-base.updated'
433
+		else
434
+			juju-log "IBM WAS Base: no new WAS Base fixpack to install"
435
+		fi
436
+	fi
437
+	
438
+	if ! charms.reactive is_state 'ibm-was-base.java.updated'; then
439
+		# If there is no prior fixpack installed (because ibm-was-base.java.updated is not
440
+		# set), do nothing since update_was_base_fixpack will handle that case.
441
+		juju-log "IBM WAS Base: no Java SDK fixpack has been installed; nothing to upgrade."
442
+		return 0
443
+	else
444
+		# If we have a fixpack packges already (because ibm-was-base.updated is set),
445
+		# we should fetch the latest fixpack packages and determine if it is new.
446
+		#  - If it is new, set our states so install_was_base_fixpack is called again
447
+		#  - If it is not new, do nothing
448
+		juju-log "IBM WAS Base: scanning for new java SDK fixpacks to install"
449
+		IBM_SDK_DIR="$CHARM_DIR/../resources/ibm_java_sdk_fp"
450
+
451
+		CUR_JAVA_SDK="$IBM_SDK_PART1_DIR/ibm_java_sdk_fp.zip"
452
+		if [ -f  $CUR_JAVA_SDK ]; then
453
+			CUR_JAVA_SDK_MD5=`md5sum "$CUR_JAVA_SDK" | awk '{print $1}'`
454
+		fi
455
+
456
+		NEW_JAVA_SDK=`resource-get 'ibm_java_sdk_fp' || echo unavailable`
457
+		
458
+		if [ "$NEW_JAVA_SDK" = "unavailable" ]; then
459
+			juju-log "IBM WAS Base: no Java SDK fixpacks to install"
460
+		elif [ -f  $NEW_JAVA_SDK ]; then
461
+			NEW_JAVA_SDK_MD5=`md5sum "$NEW_JAVA_SDK" | awk '{print $1}'`
462
+		fi
463
+		
464
+		if [ "$CUR_JAVA_SDK_MD5" != "$NEW_JAVA_SDK_MD5" ]; then
465
+			juju-log "IBM WAS Base: new JAVA SDK fixpack detected ($CUR_JAVA_SDK with $CUR_JAVA_SDK_MD5 versus $NEW_JAVA_SDK with $NEW_JAVA_SDK_MD5)"
466
+			rm -rf $CHARM_DIR/../resources/IBM_SDK/FP
467
+			remove_state 'ibm-was-base.java.updated'
468
+		else
469
+			juju-log "IBM WAS Base: no new Java SDK fixpack to install"
470
+		fi
471
+	fi
472
+}
473
+
474
+@when 'ibm-was-base.server.started' 'was-ihs.available'
475
+@when_not 'ibm-was-base.exposed'
476
+function expose_was_base_details(){
477
+	juju-log "IBM WAS Base: ************************In expose_was_base_details function ***********"
478
+	was_path=$WAS_BASE_INSTALL_PATH
479
+	was_port='9060'
480
+	host_name=`unit-get private-address`
481
+	services=$(relation_call --state=was-ihs.available services)
482
+	for service in $services; do
483
+		juju-log "IBM WAS Base: Creating the users for the IHS Admin Server"
484
+		get_cfgusername $service
485
+		juju-log "IBM WAS Base: Created IHS Admin Server Credentials."
486
+		juju-log "IBM WAS Base: Other values are was_path = $was_path hostname = $host_name service = $service ihsadminuser = $ihsadminuser ihsadmingrp = $ihsadmingrp profilename = $profile_name"
487
+		relation_call --state=was-ihs.available set_was_details $service $was_path $was_port $host_name $ihsadminuser $ihsadmingrp $ihsadminpw $profile_name || true
488
+	done
489
+	set_state 'ibm-was-base.exposed'
490
+}
491
+
492
+@when 'was-ihs.available' 'ibm-was-base.server.started'
493
+@when_not 'ibm-was-base.ihs.propagated'
494
+function propagate_ihs(){
495
+	juju-log "IBM WAS Base: ************************In propagate_ihs function ***********"
496
+	services=$(relation_call --state=was-ihs.available services)
497
+	for service in $services; do
498
+		juju-log "IBM WAS Base: Checking IHS Admin Server status."
499
+		ihsserverstatus=$(relation_call --state=was-ihs.available get_isihsstarted $service) || true
500
+		if [ "$ihsserverstatus" != "started" ] 
501
+		then
502
+			juju-log "IBM WAS Base: IHS admin server is not yet started."
503
+			continue
504
+		else
505
+			if charms.reactive is_state 'ibm-was-base.server.started'
506
+			then
507
+				stop_was_base_server $was_admin_name $was_admin_pw
508
+				set_state 'ibm-was-base.ihs.propagated'
509
+				juju-log "IBM WAS Base: IHS propagated successfully!"
510
+				status-set active "IHS propagated successfully!"
511
+			else
512
+				juju-log "IBM WAS Base: Error occured while propagating IHS"
513
+				status-set blocked "Error occured while propagating IHS"
514
+			fi
515
+		fi
516
+	done
517
+}
518
+
519
+@when 'was-ihs.departed' 
520
+function remove_relation(){
521
+	juju-log "IBM WAS Base:Removing relation from IHS."
522
+	remove_state 'ibm-was-base.exposed'
523
+	stop_was_base_server $was_admin_name $was_admin_pw
524
+	remove_state 'ibm-was-base.ihs.propagated'
525
+	juju-log "IBM WAS Base: Removed relation from IHS"
526
+}
527
+
528
+#to pass ssh keys
529
+#TODO:The below functions are written for WAS, DB2 and MQ bundle charm. 
530
+#For WAS Base stand alone deployment, these functions are not required.  
531
+@when 'wasdb.connected'
532
+@when_not 'ibm-was-base.sshconfigured'
533
+function configure_sshkeys_dbs(){
534
+	SSH_PATH=/root/.ssh
535
+	if [ ! -f  $SSH_PATH/id_rsa.pub ]; then
536
+		juju-log "IBM WAS Base: Setting up SSH keys."
537
+		ssh-keygen -t rsa -f $SSH_PATH/id_rsa -N ''
538
+	fi
539
+	key="`cat $SSH_PATH/id_rsa.pub`"
540
+	
541
+	#Pass the DB names to be created to DB2 charm
542
+	dbnames="WASDB"
543
+	juju-log "IBM WAS Base: Key is $key"
544
+	
545
+	juju-log "IBM WAS Base: DB names is $dbnames"
546
+	juju-log "IBM WAS Base: ******* In pass_ssh_keys function in WAS Base charm ***"
547
+	
548
+	# To pass the SSH key and DB names to DB2 charm
549
+	relation_call --state=wasdb.connected set_ssh_keys "$key" || true
550
+	relation_call --state=wasdb.connected set_dbs $dbnames || true
551
+	set_state 'ibm-was-base.sshconfigured'
552
+}
553
+
554
+@when 'wasdb.ready'
555
+@when 'ibm-was-base.sshconfigured'
556
+@when_not 'ibm-was-base.db2.available'
557
+function configure_db(){
558
+	juju-log "IBM WAS Base: *******In configure_db reactive function of WAS Base charm*******"
559
+	port=$(relation_call --state=wasdb.connected get_db2_port) || true
560
+	juju-log "IBM WAS Base: I got the port as $port"
561
+	if [ -z "$port" ]; then
562
+		juju-log "IBM WAS Base: No data sent yet"
563
+		return 0
564
+	fi
565
+	db2_instance=$(relation_call --state=wasdb.connected get_db2_instance_name) || true
566
+	juju-log "IBM WAS Base: I got the db2_instance as $db2_instance"
567
+	db2_path=$(relation_call --state=wasdb.available get_db2_path) || true
568
+	hostname=$(relation_call --state=wasdb.available get_db2_hostname) || true
569
+	dbusername=$(relation_call --state=wasdb.available get_dbusername) || true
570
+	dbuserpw=$(relation_call --state=wasdb.available get_dbuserpw) || true
571
+	juju-log "IBM WAS Base: I got the db2 values $db2_path $hostname $dbusername $dbuserpw"
572
+	juju-log "IBM WAS Base: Copying files from remote - Directory: $db2_path Host:$hostname"
573
+	status-set maintenance "Copying db2 jar files from remote DB2 machine"
574
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc.jar $CHARM_DIR/files || true
575
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc4.jar $CHARM_DIR/files || true
576
+	scp  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$hostname:$db2_path/V10.5/java/db2jcc_license_*.jar $CHARM_DIR/files || true
577
+	juju-log "IBM WAS Base: Copied the files to $CHARM_DIR/files"
578
+	status-set maintenance "db2 jar files copied from remote DB2 machine"
579
+	set_state 'ibm-was-base.db2.available'
580
+	
581
+}
582
+
583
+@when 'ibm-was-base.db2.available'
584
+@when_not 'wasdb.ready'
585
+function reset_all_states(){
586
+	juju-log "IBM WAS Base: Resetting all states for WAS Base charm"
587
+	remove_state 'ibm-was-base.sshconfigured'
588
+	remove_state 'ibm-was-base.db2.available'
589
+}
590
+
591
+@when 'wasdb.departed'
592
+function departed_from_db2(){
593
+	status-set maintenance "Relation broken from db2"
594
+	juju-log "IBM WAS Base: WAS Base cahrm and db2 departed"
595
+}
596
+
597
+@when 'wasmessaging.ready'
598
+@when_not 'ibm-was-base.mq.available'
599
+function get_mq_instance_details(){
600
+	juju-log "IBM WAS Base: *******In get_mq_instance_details reactive function of WAS Base charm*******"
601
+	port=$(relation_call --state=wasmessaging.connected get_mq_port) || true
602
+	if [ -z "$port" ]; then
603
+		juju-log "IBM WAS Base: No MQ data sent yet"
604
+		return 0
605
+	fi
606
+	QM_Name=$(relation_call --state=wasmessaging.available get_qm_name) || true
607
+	hostname=$(relation_call --state=wasmessaging.available get_mq_hostname) || true
608
+	Qname=$(relation_call --state=wasmessaging.available get_qname) || true
609
+	status-set active "IBM WAS Base: Got MQ instance details."
610
+	juju-log "IBM WAS Base: values of MQ in WAS Charm port=$port QM_Name=$QM_Name hostname=$hostname Qname=$Qname"
611
+	set_state 'ibm-was-base.mq.available'
612
+}
613
+
614
+@when 'ibm-was-base.mq.available'
615
+@when_not 'wasmessaging.ready'
616
+function reset_all_mq_relation_states(){
617
+	juju-log "IBM WAS Base: Resetting all MQ states for WAS Base charm"
618
+	remove_state 'ibm-was-base.mq.available'
619
+}
620
+
621
+@when 'wasmessaging.departed'
622
+function departed_from_mq(){
623
+	status-set maintenance "IBM WAS Base: Relation broken from mq"
624
+	juju-log "IBM WAS Base: WAS Base cahrm and mq departed"
625
+}
626
+
627
+reactive_handler_main
Back to file index

requirements.txt

1
--- 
2
+++ requirements.txt
3
@@ -0,0 +1,2 @@
4
+flake8
5
+pytest
Back to file index

revision

1
--- 
2
+++ revision
3
@@ -0,0 +1 @@
4
+0
Back to file index

tests/01-deploy.py

 1
--- 
 2
+++ tests/01-deploy.py
 3
@@ -0,0 +1,42 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+import amulet
 7
+import re
 8
+import requests
 9
+import unittest
10
+
11
+seconds_to_wait = 20000
12
+
13
+
14
+class TestDeploy(unittest.TestCase):
15
+    """
16
+    Deployment test for the IBM WAS Base charm.
17
+    """
18
+    def setUp(self):
19
+        self.d = amulet.Deployment(series='trusty')
20
+        self.d.add('ibm-was-base', 'cs:~ibmcharmers/xenial/ibm-was-base')
21
+        self.d.setup(seconds_to_wait)
22
+        self.d.sentry.wait(seconds_to_wait)
23
+
24
+    def test_deploy_with_placeholder_resource(self):
25
+        # The status message when using placeholder resources will include the
26
+        # string "ibm_was_base_installer resource". If we see that, the test is
27
+        # successful.
28
+        sentry_re = re.compile('ibm_was_base_installer resource')
29
+        self.d.sentry.wait_for_messages({"ibm-was-base": sentry_re})
30
+
31
+    def test_was_base_deployed(self):
32
+        self.assertTrue(self.d.deployed)
33
+        unit = self.d.sentry['ibm-was-base'][0]
34
+        state_was_base = unit.info['agent-state']
35
+        print('WAS Base Server is %s' % state_was_base)
36
+        url = 'http://%s:9060/ibm/console' % unit.info['public-address']
37
+        https_url = 'https://%s:9043/ibm/console' % unit.info['public-address']
38
+        response = requests.get(url, verify=False)
39
+        response = requests.get(https_url, verify=False)
40
+        # Raise an exception if the url was not a valid web page.
41
+        response.raise_for_status()
42
+
43
+
44
+if __name__ == '__main__':
45
+    unittest.main()
Back to file index

tests/tests.yaml

1
--- 
2
+++ tests/tests.yaml
3
@@ -0,0 +1,5 @@
4
+packages:
5
+  - amulet
6
+  - python3
7
+  - unzip
8
+  - pwgen
Back to file index

tox.ini

 1
--- 
 2
+++ tox.ini
 3
@@ -0,0 +1,12 @@
 4
+[tox]
 5
+skipsdist=True
 6
+envlist = py34, py35
 7
+skip_missing_interpreters = True
 8
+
 9
+[testenv]
10
+commands = py.test -v
11
+deps =
12
+    -r{toxinidir}/requirements.txt
13
+
14
+[flake8]
15
+exclude=docs