~ibmcharmers/trusty/ibm-was-nd-dm

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

CPP?: No
OIL?: No

Hello Team,

Here is a new charm for IBM WAS ND DM for review.

To test the IBM WAS ND DM, we need the IBM WAS ND charm as well. As per the design changes, the WAS ND charm has been split into three charms:
IBM WAS ND
IBM WAS ND DM
IBM WAS ND Node.

We need all three charms to be tested to have a WAS ND dynamic cluster. Here we are using IBM WAS ND charm as a common layer for IBM WAS ND DM and IBM WAS ND Node charms.

Deployable ibm-ibm-was-nd-dm layer charm can be found in the below repository.
Repo : https://code.launchpad.net/~ibmcharmers/charms/trusty/ibm-was-nd-dm/trunk

And, its source code can be found in the below repository
Repo : https://code.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-was-nd-dm/trunk

The charm has been pushed into charm store as well
branch : cs:~ibmcharmers/trusty/ibm-was-nd-dm-10

Thanks


Tests

Substrate Status Results Last Updated
gce FAIL http://juju-ci.vapour.ws/job/charm-bundle-test-gce/262/console 6 months ago
aws FAIL http://juju-ci.vapour.ws/job/charm-bundle-test-aws/7546/console 6 months ago
lxc FAIL Test Results 6 months ago

Voted: -1
petevg wrote 3 months ago
Thank you for submitting this charm!

Unfortunately, I cannot approve it due to the following reasons:

1) If no operator action is taken, the service runs with a default, publicly available admin password. (See: "Must not run any services using default passwords" below)
2) The charm behaves in a non standard way when the admin user and password changes. Operators are instructed to remove relations before changing the username and password, then re-add the relations after the password has been changed. I would expect related charms to handle this change automatically, based on a relation data changed event.

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.
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.
Should be built using charm layers. petevg
Should use Juju Resources to deliver required payloads.

Testing and Quality

charm proof must pass without errors or warnings. petevg
Must include passing unit, functional, or integration tests. petevg
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).
Must be self contained unless the charm is a proxy for an existing cloud service, e.g. ec2-elb charm.
Must not use symlinks.
Bundles must only use promulgated charms, they cannot reference charms in personal namespaces.
Must call Juju hook tools (relation-*, unit-*, config-*, etc) without a hard coded path.
Should include a tests.yaml for all integration tests.

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
  • 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.
Should avoid running services as root.

Source Diff

Files changed 128

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,210 @@
  4
+Charm for IBM WebSphere Application Server ND DM (WAS ND DM) 8.5.5.0
  5
+
  6
+## Overview
  7
+
  8
+IBM WebSphere Application Server Network Deployment provides an advanced, flexible runtime environment for large-scale application deployments. It offers near-continuous availability with advanced performance and management capabilities for mission-critical applications. For details on IBM Websphere Application Server Network Deployment,  as well as information on purchasing, please visit [Product Page][WAS-ND-product-page] and at the [Passport Advantage Site] [Passport-Advantage]. More information available at the [IBM Knowledge Center] [WAS-ND-Infocenter].
  9
+
 10
+`IBM WebSphere Application Server ND DM`
 11
+
 12
+This charm is using IBM WAS ND charm as a basic layer to install IBM WAS ND Software. After WAS ND installation, this charm will creates deployment manager profile to configure dynamic cluster setup.
 13
+
 14
+**Please note that the WAS ND charms (WAS ND, WAS ND DM and WAS ND Node) will create IBM WAS ND dynamic cluster.
 15
+
 16
+## Prerequisites
 17
+
 18
+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) required by this charm. Download your licensed IBM WebSphere Application Server ND packages from the [Product Page][WAS-ND-8.5.5.0-download].
 19
+
 20
+### Packages for Ubuntu on AMD64 (x86_64):
 21
+
 22
+IBM Installation Manager 1.8 (`agent.installer.linux.gtk.x86_64_1.8.3000.20150606_0047.zip`)
 23
+
 24
+IBM WAS ND 8.5.5 part1 (WASND_v8.5.5_1of3.zip)
 25
+
 26
+IBM WAS ND 8.5.5 part2 (WASND_v8.5.5_2of3.zip)
 27
+
 28
+IBM WAS ND 8.5.5 part3 (WASND_v8.5.5_3of3.zip)
 29
+
 30
+### Packages for Ubuntu on Power (ppc64le):
 31
+
 32
+IBM Installation Manager 1.8 (`agent.installer.linux.gtk.ppc64le_1.8.3000.20150606_0047.zip`)
 33
+
 34
+IBM WAS ND 8.5.5 part1 (WAS_ND_V8.5.5_FOR_POWERLE_ML.zip)
 35
+
 36
+IBM WAS ND 8.5.5 part2 (WASND_V8.5.5_2OF_2_FOR_POWERLE_ML.zip)
 37
+
 38
+## Usage
 39
+
 40
+To use this charm, you must agree to the Terms of Use. You can view the full license for `IBM Installation Manager` and `IBM WebSphere Application Server Network Deployment` products by visiting the [im-license-info](http://www-03.ibm.com/software/sla/sladb.nsf/displaylis/39AFC1C1D485C4E085257E7300548B05?OpenDocument) and [was-nd-license-info] [license-info] page.
 41
+
 42
+Search for "IBM WebSphere Application Server Network Deployment" and choose the license that applies to the version you are using.
 43
+
 44
+
 45
+## Memory and Disk Requirements
 46
+
 47
+Minimum 2.0 GB of disk space for installed image and Minimum 1 GB of physical memory recommended. 
 48
+
 49
+WebSphere Application Server ND Charm requires 15 GB of root disk to download packages and install the IBM WebSphere Application Server ND Software. By default available root disk space is 8GB.
 50
+
 51
+To request a larger root disk, run the following command:
 52
+
 53
+		juju set-constraints root-disk=15G
 54
+
 55
+### Deploy
 56
+
 57
+1. Run the following commands to deploy this charm:
 58
+
 59
+
 60
+		juju deploy ibm-was-nd-dm --resource ibm_im_installer=</path/to/ibm_im_installer.zip> --resource ibm_was_nd_installer1=</path/to/ibm_was_nd_installer1.zip> --resource ibm_was_nd_installer2=</path/to/ibm_was_nd_installer2.zip> --resource ibm_was_nd_installer3=</path/to/ibm_was_nd_installer3.zip> 
 61
+
 62
+
 63
+**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.
 64
+
 65
+To accept the terms:
 66
+
 67
+		juju agree ibm-im/1 ibm-was-nd/1
 68
+		juju deploy ibm-was-nd-dm
 69
+
 70
+
 71
+2. To create deployment manager profile, `manageprofiles` command needs values for username and password. To provide these run the following command:
 72
+
 73
+		juju config ibm-was-nd-dm dm_admin_user=<user_name>
 74
+		juju config ibm-was-nd-dm dm_admin_pw=<password>
 75
+
 76
+	For eg:
 77
+
 78
+		juju config ibm-was-nd-dm was_admin_user="admin1"
 79
+		juju config ibm-was-nd-dm was_admin_pw="admin123"
 80
+
 81
+
 82
+**Note**: If any relation established between WAS ND DM and other charms, Please remove the relation before changing these config options.
 83
+
 84
+If any of these values is not set, then the charm will use default values for username/password. Once WAS ND DM charm is deployed successfully, user can also delete existing profile and create new DM profile by setting new values for these configuration options.
 85
+
 86
+
 87
+3. To Expose the IBM WAS ND DM Service to public, run the following command:
 88
+
 89
+		juju expose ibm-was-nd-dm
 90
+
 91
+
 92
+### Upgrade
 93
+
 94
+Once deployed, user can upgrade the existing installation by installing fixpacks:
 95
+
 96
+If user wants to upgrade existing installation of Installtion Manger, run the following command:
 97
+
 98
+		juju attach ibm-was-nd-dm ibm_im_fixpack=</path/to/fixpack.zip>
 99
+
100
+To upgrade WAS ND DM installation:
101
+	
102
+		juju attach ibm-was-nd-dm ibm_was_nd_fp1=</path/to/ibm_was_nd_fp1.zip>
103
+		juju attach ibm-was-nd-dm ibm_was_nd_fp2=</path/to/ibm_was_nd_fp2.zip>
104
+
105
+**Note**: WAS ND DM and WAS ND Node installation should be in same level(If you want to upgrade WAS ND DM installation after cluster setup, make sure that WAS ND Node also upgraded with same fixpack version).
106
+
107
+
108
+### Verification
109
+
110
+After installing IBM WebSphere Application Server ND DM, use your web browser to see the WebSphere Application Server ND DM console. The URLs for WAS ND DM console are:
111
+
112
+		http://was-nd-dm-host:9060/ibm/console
113
+
114
+									OR
115
+
116
+		https://was-nd-dm-host:9043/ibm/console
117
+
118
+Here was-nd-dm-host represents the public ip address of your machine, where WAS ND DM is installed. 
119
+
120
+To login WAS ND DM console use user name and password created in step 2 in `Deploy` section.
121
+
122
+
123
+## Relating with IBM WAS ND Node charm
124
+
125
+When `IBM WAS ND DM` charm is related to `IBM WAS ND Node` charm, WAS ND DM charm will create dynamic cluster and WAS ND Node charm will federate the nodes to deployment manager(DM). Once all nodes federated to DM, cluster will be started.
126
+
127
+To add a relation, run the following command:
128
+
129
+		juju add-relation ibm-was-nd-dm ibm-was-nd-node
130
+
131
+To remove the relation, run the following command:
132
+
133
+		juju remove-relation ibm-was-nd-dm ibm-was-nd-node
134
+
135
+## Relating with IBM HTTP Server charm
136
+
137
+Install a web server, such as IBM Http Server in the machine where IBM WebSphere Application Server Base installed. It provides the more configuration options, additional layer of security and load balancing for applications deployed on WAS ND cluster. Installing a web server plug-in enables the web server to communicate with the application server.
138
+
139
+Here IBM WAS ND DM charm is related to IBM HTTP Server(IHS) subordinate charm. To add a relation, run the following command:
140
+
141
+		juju add-relation ibm-was-nd-dm ibm-http
142
+
143
+When IBM WAS ND DM charm is related to IBM HTTP Server charm, `IBM Http Server`, `Web Server Plug-ins for WebSphere Application Server` and `WebSphere Customization Toolbox` will be installed and configured. The web server definition is automatically created and configured during the configuration of the plug-in. Once IHS configured and admin server started, server status will be sent to the WAS ND DM charm to automatically propagate the plugin.xml file to IHS server. This plugin.xml file contains the WAS ND cluster details to redirect incoming requests to the cluster members.
144
+
145
+### Verification
146
+
147
+After establishing relation between IBM WAS ND DM and IBM Http Server charm, you can verify by accessing the sample servlet available in WAS ND using IHS URL and IHS running port 80:
148
+
149
+		http://ihs-host-name:80/snoop
150
+
151
+Here ihs-host-name represents the public ip address of your machine, where WAS Base and IHS deployed(IHS is subordinate charm so WAS Base and IHS charms will be deployed on same machine). 'snoop' is the sample servlet on WAS Base server to test the connection between WAS Base and IHS.
152
+
153
+To remove the relation, run the following command:
154
+
155
+		juju remove-relation ibm-was-nd-dm ibm-http
156
+
157
+## Relating with other consumer charms
158
+
159
+IBM WAS ND DM charm can be related to other consumer charms using the following command.
160
+
161
+		juju add-relation ibm-was-nd-dm <consumer-charm>
162
+
163
+When IBM WAS ND DM charm is related to any consumer charm such as IBM WXS, it provides WAS ND DM installation path, profile name, username and password to start/stop the deployment manager.
164
+
165
+
166
+## Configuration
167
+
168
+`was_admin_user`
169
+
170
+User name to login WAS ND deployment manager console
171
+
172
+`was_admin_pw`
173
+
174
+The password used to login WAS ND deployment manager console.
175
+
176
+
177
+## IBM WebSphere Application Server Network Deployment Information
178
+
179
+(1) General Information 
180
+
181
+1. Details about IBM WAS ND software available at [IBM Knowledge Center] [WAS-ND-Infocenter].
182
+
183
+2. Information on procuring WAS ND product is available at the [Passport Advantage site] [Passport-Advantage]
184
+
185
+(2) Download Information
186
+
187
+1. Details about WAS 8.5.5.0 download available [here] [[WAS-ND-8.5.5.0-download]
188
+
189
+2. Information about installation of WAS ND on 'ppc64le' based machine available [here] [WAS-ND-for-ppc64le]  
190
+
191
+3. More information about IBM IM Downloadable file and WAS ND downloadable files available [here] [IBM-IM-more-info]
192
+
193
+## Contact Information
194
+
195
+For issues with this charm, please contact IBM Juju Support team <jujusupp@us.ibm.com>
196
+
197
+<!-- Links -->
198
+
199
+[WAS-ND-Infocenter]: http://www-01.ibm.com/support/knowledgecenter/SSAW57_8.5.5/as_ditamaps/was855_welcome_ndmp.html?lang=en
200
+
201
+[WAS-ND-product-page]: http://www-03.ibm.com/software/products/en/appserv-wasnd
202
+
203
+[WAS-ND-8.5.5.0-download]: http://www-01.ibm.com/support/docview.wss?uid=swg27038624
204
+
205
+[Passport-Advantage]: http://www-01.ibm.com/software/passportadvantage/
206
+
207
+[IBM-IM-more-info]: http://www-01.ibm.com/support/docview.wss?uid=swg24038380
208
+
209
+[WAS-ND-for-ppc64le]: http://www-304.ibm.com/support/knowledgecenter/SSEQTP_8.5.5/com.ibm.websphere.installation.nd.doc/ae/cins_offerings.html?lang=en-us
210
+
211
+[WAS-ppc64le-fixpack-info]: http://www-01.ibm.com/support/docview.wss?uid=swg24040035
212
+
213
+[license-info]: http://www-03.ibm.com/software/sla/sladb.nsf/lilookup/6CCA78CD870DCA5985257D90005A5351?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,65 @@
 4
+"options":
 5
+  "curl_url":
 6
+    "type": "string"
 7
+    "default": ""
 8
+    "description": |
 9
+      Location of the IBM product installation file(s). This should be a URL
10
+      that curl can use to download files. Multiple URLs should be separated
11
+      by a space. NOTE: cryptographic verification is required and must be
12
+      specified as part of the URL query string with the key a valid hash
13
+      algorithms md5, sha256, or sha512, and the the checksum value itself
14
+      (http://<url>?[md5|sha256|sha512]=<checksum>).
15
+      For example:
16
+        'http://example.com/file.tgz?sha256=<sum>'
17
+        'sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>'
18
+  "curl_opts":
19
+    "type": "string"
20
+    "default": ""
21
+    "description": |
22
+      The options passed to the 'curl' command when fetching files from
23
+      curl_url. For example:
24
+        '-u <user:password>'
25
+  "license_accepted":
26
+    "type": "boolean"
27
+    "default": !!bool "false"
28
+    "description": |
29
+      Some IBM charms require acceptance of a license before installation
30
+      can proceed. If required, setting this option to True indicates that you
31
+      have read and accepted the IBM terms and conditions found in the license
32
+      file referenced by the charm.
33
+  "extra_packages":
34
+    "description": "Space separated list of extra deb packages to install.\n"
35
+    "type": "string"
36
+    "default": ""
37
+  "package_status":
38
+    "default": "install"
39
+    "type": "string"
40
+    "description": "The status of service-affecting packages will be set to this value\
41
+      \ in the dpkg database. Valid values are \"install\" and \"hold\".\n"
42
+  "install_sources":
43
+    "description": "List of extra apt sources, per charm-helpers standard format (a\
44
+      \ yaml list of strings encoded as a string). Each source may be either a line\
45
+      \ that can be added directly to sources.list(5), or in the form ppa:<user>/<ppa-name>\
46
+      \ for adding Personal Package Archives, or a distribution component to enable.\n"
47
+    "type": "string"
48
+    "default": ""
49
+  "install_keys":
50
+    "description": "List of signing keys for install_sources package sources, per\
51
+      \ charmhelpers standard format (a yaml list of strings encoded as a string).\
52
+      \ The keys should be the full ASCII armoured GPG public keys. While GPG key\
53
+      \ ids are also supported and looked up on a keyserver, operators should be aware\
54
+      \ that this mechanism is insecure. null can be used if a standard package signing\
55
+      \ key is used that will already be installed on the machine, and for PPA sources\
56
+      \ where the package signing key is securely retrieved from Launchpad.\n"
57
+    "type": "string"
58
+    "default": ""
59
+  "dm_admin_user":
60
+    "type": "string"
61
+    "default": "wsadmin"
62
+    "description": |
63
+      Admin user name to log in WAS DM console.
64
+  "dm_admin_pw":
65
+    "type": "string"
66
+    "default": "wsadmin"
67
+    "description": |
68
+      The password used to log in WAS DM console.
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

deps/layer/layer-apt/.gitignore

1
--- 
2
+++ deps/layer/layer-apt/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-apt/README.md

  1
--- 
  2
+++ deps/layer/layer-apt/README.md
  3
@@ -0,0 +1,195 @@
  4
+# Apt layer
  5
+
  6
+The Apt layer for Juju enables layered charms to more easily deal with
  7
+deb packages and apt sources in a simple and efficient manner. It
  8
+provides consistent configuration for operators, allowing them to
  9
+easily specify custom apt sources and additional debs required for
 10
+their particular installations.
 11
+
 12
+## Configuration
 13
+
 14
+The charm may provide defaults for these service configuration
 15
+(config.yaml) options, and the operator may override them as required.
 16
+
 17
+* `extra_packages`
 18
+
 19
+  A space separated list of additional deb packages to install on
 20
+  each unit.
 21
+
 22
+* `package_status`
 23
+
 24
+  'install' or 'hold'. When set to hold, packages installed using
 25
+  the Apt layer API will be pinned, so that they will not be
 26
+  automatically upgraded when package updates are performed. 'hold'
 27
+  is particularly useful for allowing a service such as Landscape
 28
+  to automatically apply security updates to most of the system,
 29
+  whilst holding back any potentially service affecting updates.
 30
+
 31
+* `install_sources`
 32
+
 33
+  A list of apt sources containing the packages that need to be installed.
 34
+  Each source may be either a line that can be added directly to
 35
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 36
+  Personal Package Archives, or a distribution component to enable.
 37
+  The list is a yaml list, encoded as a string. The nicest way of
 38
+  declaring this in a yaml file looks like the following (in particular,
 39
+  the | character indicates that the value is a multiline string):
 40
+
 41
+  ```yaml
 42
+  install_sources: |
 43
+      - ppa:stub/cassandra
 44
+      - deb http://www.apache.org/dist/cassandra/debian 21x main
 45
+  ```
 46
+
 47
+* `install_keys`
 48
+
 49
+  A list of GPG signing keys to accept. There needs to be one entry
 50
+  per entry in install_sources. null may be used if no keep is
 51
+  needed, which is the case for PPAs and for the standard Ubuntu
 52
+  archives. Keys should be full ASCII armoured GPG public keys.
 53
+  GPG key ids are also accepted, but in most environments this
 54
+  mechanism is not secure. The install_keys list, like
 55
+  install_sources, must also be a yaml formatted list encoded as
 56
+  a string:
 57
+
 58
+  ```yaml
 59
+  install_keys: |
 60
+      - null
 61
+      - |
 62
+          -----BEGIN PGP PUBLIC KEY BLOCK-----
 63
+          Version: GnuPG v1
 64
+
 65
+          mQINBFQJvgUBEAC0KcYCTj0hd15p4fiXBsbob0sKgsvN5Lm7N9jzJWlGshJ0peMi
 66
+          kH8YhDXw5Lh+mPEHksL7t1L8CIr1a+ntns/Opt65ZPO38ENVkOqEVAn9Z5sIoZsb
 67
+          AUeLlJzSeRLTKhcOugK7UcsQD2FHnMBJz50bxis9X7pjmnc/tWpjAGJfaWdjDIo=
 68
+          =yiQ4
 69
+          -----END PGP PUBLIC KEY BLOCK-----
 70
+  ```
 71
+
 72
+## Usage
 73
+
 74
+Queue packages for installation, and have handlers waiting for
 75
+these packages to finish being installed:
 76
+
 77
+```python
 78
+import charms.apt
 79
+
 80
+@hook('install')
 81
+def install():
 82
+    charms.apt.queue_install(['git'])
 83
+
 84
+@when_not('apt.installed.gnupg')
 85
+def install_gnupg():
 86
+    charms.apt.queue_install(['gnupg'])
 87
+
 88
+@when('apt.installed.git')
 89
+@when('apt.installed.gnupg')
 90
+def grabit():
 91
+    clone_repo()
 92
+    validate_repo()
 93
+```
 94
+
 95
+### API
 96
+
 97
+Several methods are exposed in the charms.apt Python package.
 98
+
 99
+* `add_source(source, key=None)`
100
+
101
+  Add an apt source.
102
+
103
+  A source may be either a line that can be added directly to
104
+  sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
105
+  Personal Package Archives, or a distribution component to enable.
106
+
107
+  The package signing key should be an ASCII armoured GPG key. While
108
+  GPG key ids are also supported, the retrieval mechanism is insecure.
109
+  There is no need to specify the package signing key for PPAs or for
110
+  the main Ubuntu archives.
111
+
112
+  It is preferable if charms do not call this directly to hard
113
+  coded apt sources, but instead have these sources listed
114
+  as defaults in the install_sources config option. This allows
115
+  operators to mirror your packages to internal archives and
116
+  deploy your charm in environments without network access.
117
+
118
+  Sets the `apt.needs_update` reactive state.
119
+
120
+* `queue_install(packages, options=None)`
121
+
122
+  Queue one or more deb packages for install. The actual package
123
+  installation will be performed later by a handler in the
124
+  apt layer. The `apt.installed.{name}` state will be set once
125
+  the package installed (one state for each package).
126
+
127
+  If a package has already been installed it will not be reinstalled.
128
+
129
+  If a package has already been queued it will not be requeued, and
130
+  the install options will not be changed.
131
+
132
+* `installed()`
133
+
134
+  Returns the set of deb packages installed by this layer.
135
+
136
+* `purge(packages)`
137
+
138
+  Purge one or more deb packages from the system
139
+
140
+
141
+### Extras
142
+
143
+These methods are called automatically by the reactive framework as
144
+reactive state demands. However, you can also invoke them directly
145
+if you want the operation done right now.
146
+
147
+* `update()`
148
+
149
+  Update the apt cache. Removes the `apt.needs_update` state.
150
+
151
+
152
+* `install_queued()`
153
+
154
+  Installs deb packages queued for installation. On success, removes
155
+  the `apt.queued_installs` state, sets the `apt.installed.{packagename}`
156
+  state for each installed package, and returns True. On failure,
157
+  sets the unit workload status to blocked and returns False.
158
+  The package installs remain queued.
159
+
160
+
161
+### Layer Options
162
+
163
+Packages can be specified at charm-build time in `layer.yaml`. List the
164
+packages in the 'basic' or 'apt' sections.
165
+
166
+```yaml
167
+includes:
168
+  - layer:basic
169
+  - layer:apt
170
+options:
171
+  basic:
172
+    packages:
173
+      - python3-psycopg2
174
+  apt:
175
+    packages:
176
+      - git
177
+      - bzr
178
+```
179
+
180
+Packages required to import your Python reactive handlers should go
181
+under 'basic'. These get installed by the base layer very early during
182
+charm bootstrap, and only packages available in the main Ubuntu archive
183
+can go here. Other packages should go under 'apt'. These will be
184
+installed later, after custom apt sources such as PPAs have been added
185
+from the `install_sources` configuration option.
186
+
187
+
188
+## Support
189
+
190
+This layer is maintained on Launchpad by
191
+Stuart Bishop (stuart.bishop@canonical.com).
192
+
193
+Code is available using git at git+ssh://git.launchpad.net/layer-apt.
194
+
195
+Bug reports can be made at https://bugs.launchpad.net/layer-apt.
196
+
197
+Queries and comments can be made on the Juju mailing list, Juju IRC
198
+channels, or at https://answers.launchpad.net/layer-apt.
Back to file index

deps/layer/layer-apt/config.yaml

 1
--- 
 2
+++ deps/layer/layer-apt/config.yaml
 3
@@ -0,0 +1,34 @@
 4
+options:
 5
+  extra_packages:
 6
+    description: >
 7
+        Space separated list of extra deb packages to install.
 8
+    type: string
 9
+    default: ""
10
+  package_status:
11
+    default: "install"
12
+    type: string
13
+    description: >
14
+        The status of service-affecting packages will be set to this
15
+        value in the dpkg database. Valid values are "install" and "hold".
16
+  install_sources:
17
+    description: >
18
+        List of extra apt sources, per charm-helpers standard
19
+        format (a yaml list of strings encoded as a string). Each source
20
+        may be either a line that can be added directly to
21
+        sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
22
+        Personal Package Archives, or a distribution component to enable.
23
+    type: string
24
+    default: ""
25
+  install_keys:
26
+    description: >
27
+        List of signing keys for install_sources package sources, per
28
+        charmhelpers standard format (a yaml list of strings encoded as
29
+        a string). The keys should be the full ASCII armoured GPG public
30
+        keys. While GPG key ids are also supported and looked up on a
31
+        keyserver, operators should be aware that this mechanism is
32
+        insecure. null can be used if a standard package signing key is
33
+        used that will already be installed on the machine, and for PPA
34
+        sources where the package signing key is securely retrieved from
35
+        Launchpad.
36
+    type: string
37
+    default: ""
Back to file index

deps/layer/layer-apt/copyright

 1
--- 
 2
+++ deps/layer/layer-apt/copyright
 3
@@ -0,0 +1,15 @@
 4
+Copyright 2015-2016 Canonical Ltd.
 5
+
 6
+This file is part of the Apt layer for Juju.
 7
+
 8
+This program is free software: you can redistribute it and/or modify
 9
+it under the terms of the GNU General Public License version 3, as
10
+published by the Free Software Foundation.
11
+
12
+This program is distributed in the hope that it will be useful, but
13
+WITHOUT ANY WARRANTY; without even the implied warranties of
14
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+PURPOSE.  See the GNU General Public License for more details.
16
+
17
+You should have received a copy of the GNU General Public License
18
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
Back to file index

deps/layer/layer-apt/layer.yaml

1
--- 
2
+++ deps/layer/layer-apt/layer.yaml
3
@@ -0,0 +1,5 @@
4
+defines:
5
+  packages:
6
+    type: array
7
+    default: []
8
+    description: Additional packages to be installed at time of bootstrap
Back to file index

deps/layer/layer-apt/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-apt/lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

deps/layer/layer-apt/lib/charms/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/lib/charms/apt.py
  3
@@ -0,0 +1,182 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import itertools
 28
+import subprocess
 29
+
 30
+from charmhelpers import fetch
 31
+from charmhelpers.core import hookenv, unitdata
 32
+from charms import reactive
 33
+
 34
+
 35
+__all__ = ['add_source', 'update', 'queue_install', 'install_queued',
 36
+           'installed', 'purge', 'ensure_package_status']
 37
+
 38
+
 39
+def add_source(source, key=None):
 40
+    '''Add an apt source.
 41
+
 42
+    Sets the apt.needs_update state.
 43
+
 44
+    A source may be either a line that can be added directly to
 45
+    sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 46
+    Personal Package Archives, or a distribution component to enable.
 47
+
 48
+    The package signing key should be an ASCII armoured GPG key. While
 49
+    GPG key ids are also supported, the retrieval mechanism is insecure.
 50
+    There is no need to specify the package signing key for PPAs or for
 51
+    the main Ubuntu archives.
 52
+    '''
 53
+    # Maybe we should remember which sources have been added already
 54
+    # so we don't waste time re-adding them. Is this time significant?
 55
+    fetch.add_source(source, key)
 56
+    reactive.set_state('apt.needs_update')
 57
+
 58
+
 59
+def queue_install(packages, options=None):
 60
+    """Queue one or more deb packages for install.
 61
+
 62
+    The `apt.installed.{name}` state is set once the package is installed.
 63
+
 64
+    If a package has already been installed it will not be reinstalled.
 65
+
 66
+    If a package has already been queued it will not be requeued, and
 67
+    the install options will not be changed.
 68
+
 69
+    Sets the apt.queued_installs state.
 70
+    """
 71
+    if isinstance(packages, str):
 72
+        packages = [packages]
 73
+    # Filter installed packages.
 74
+    store = unitdata.kv()
 75
+    queued_packages = store.getrange('apt.install_queue.', strip=True)
 76
+    packages = {package: options for package in packages
 77
+                if not (package in queued_packages or
 78
+                        reactive.helpers.is_state('apt.installed.' + package))}
 79
+    if packages:
 80
+        unitdata.kv().update(packages, prefix='apt.install_queue.')
 81
+        reactive.set_state('apt.queued_installs')
 82
+
 83
+
 84
+def installed():
 85
+    '''Return the set of deb packages completed install'''
 86
+    return set(state.split('.', 2)[2] for state in reactive.bus.get_states()
 87
+               if state.startswith('apt.installed.'))
 88
+
 89
+
 90
+def purge(packages):
 91
+    """Purge one or more deb packages from the system"""
 92
+    fetch.apt_purge(packages, fatal=True)
 93
+    store = unitdata.kv()
 94
+    store.unsetrange(packages, prefix='apt.install_queue.')
 95
+    for package in packages:
 96
+        reactive.remove_state('apt.installed.{}'.format(package))
 97
+
 98
+
 99
+def update():
100
+    """Update the apt cache.
101
+
102
+    Removes the apt.needs_update state.
103
+    """
104
+    status_set(None, 'Updating apt cache')
105
+    fetch.apt_update(fatal=True)  # Friends don't let friends set fatal=False
106
+    reactive.remove_state('apt.needs_update')
107
+
108
+
109
+def install_queued():
110
+    '''Installs queued deb packages.
111
+
112
+    Removes the apt.queued_installs state and sets the apt.installed state.
113
+
114
+    On failure, sets the unit's workload state to 'blocked' and returns
115
+    False. Package installs remain queued.
116
+
117
+    On success, sets the apt.installed.{packagename} state for each
118
+    installed package and returns True.
119
+    '''
120
+    store = unitdata.kv()
121
+    queue = sorted((options, package)
122
+                   for package, options in store.getrange('apt.install_queue.',
123
+                                                          strip=True).items())
124
+
125
+    installed = set()
126
+    for options, batch in itertools.groupby(queue, lambda x: x[0]):
127
+        packages = [b[1] for b in batch]
128
+        try:
129
+            status_set(None, 'Installing {}'.format(','.join(packages)))
130
+            fetch.apt_install(packages, options, fatal=True)
131
+            store.unsetrange(packages, prefix='apt.install_queue.')
132
+            installed.update(packages)
133
+        except subprocess.CalledProcessError:
134
+            status_set('blocked',
135
+                       'Unable to install packages {}'
136
+                       .format(','.join(packages)))
137
+            return False  # Without setting reactive state.
138
+
139
+    for package in installed:
140
+        reactive.set_state('apt.installed.{}'.format(package))
141
+
142
+    reactive.remove_state('apt.queued_installs')
143
+    return True
144
+
145
+
146
+def ensure_package_status():
147
+    '''Hold or unhold packages per the package_status configuration option.
148
+
149
+    All packages installed using this module and handlers are affected.
150
+
151
+    An mechanism may be added in the future to override this for a
152
+    subset of installed packages.
153
+    '''
154
+    packages = installed()
155
+    if not packages:
156
+        return
157
+    config = hookenv.config()
158
+    package_status = config['package_status']
159
+    changed = reactive.helpers.data_changed('apt.package_status',
160
+                                            (package_status, sorted(packages)))
161
+    if changed:
162
+        if package_status == 'hold':
163
+            hookenv.log('Holding packages {}'.format(','.join(packages)))
164
+            fetch.apt_hold(packages)
165
+        else:
166
+            hookenv.log('Unholding packages {}'.format(','.join(packages)))
167
+            fetch.apt_unhold(packages)
168
+    reactive.remove_state('apt.needs_hold')
169
+
170
+
171
+def status_set(state, message):
172
+    """Set the unit's workload status.
173
+
174
+    Set state == None to keep the same state and just change the message.
175
+    """
176
+    if state is None:
177
+        state = hookenv.status_get()[0]
178
+        if state == 'unknown':
179
+            state = 'maintenance'  # Guess
180
+    if state in ('error', 'blocked'):
181
+        lvl = hookenv.WARNING
182
+    else:
183
+        lvl = hookenv.INFO
184
+    hookenv.status_set(state, message)
185
+    hookenv.log('{}: {}'.format(state, message), lvl)
Back to file index

deps/layer/layer-apt/reactive/apt.py

  1
--- 
  2
+++ deps/layer/layer-apt/reactive/apt.py
  3
@@ -0,0 +1,131 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import subprocess
 28
+
 29
+from charmhelpers import fetch
 30
+from charmhelpers.core import hookenv
 31
+from charmhelpers.core.hookenv import WARNING
 32
+from charms import layer
 33
+from charms import reactive
 34
+from charms.reactive import when, when_not
 35
+
 36
+import charms.apt
 37
+
 38
+
 39
+@when('apt.needs_update')
 40
+def update():
 41
+    charms.apt.update()
 42
+
 43
+
 44
+@when('apt.queued_installs')
 45
+@when_not('apt.needs_update')
 46
+def install_queued():
 47
+    charms.apt.install_queued()
 48
+
 49
+
 50
+@when_not('apt.queued_installs')
 51
+def ensure_package_status():
 52
+    charms.apt.ensure_package_status()
 53
+
 54
+
 55
+def filter_installed_packages(packages):
 56
+    # Don't use fetch.filter_installed_packages, as it depends on python-apt
 57
+    # and not available if the basic layer's use_site_packages option is off
 58
+    # TODO: Move this to charm-helpers.fetch
 59
+    cmd = ['dpkg-query', '--show', r'--showformat=${Package}\n']
 60
+    installed = set(subprocess.check_output(cmd,
 61
+                                            universal_newlines=True).split())
 62
+    return set(packages) - installed
 63
+
 64
+
 65
+def clear_removed_package_states():
 66
+    """On hook startup, clear install states for removed packages."""
 67
+    removed = filter_installed_packages(charms.apt.installed())
 68
+    if removed:
 69
+        hookenv.log('{} missing packages ({})'.format(len(removed),
 70
+                                                      ','.join(removed)),
 71
+                    WARNING)
 72
+        for package in removed:
 73
+            reactive.remove_state('apt.installed.{}'.format(package))
 74
+
 75
+
 76
+def configure_sources():
 77
+    """Add user specified package sources from the service configuration.
 78
+
 79
+    See charmhelpers.fetch.configure_sources for details.
 80
+    """
 81
+    config = hookenv.config()
 82
+
 83
+    # We don't have enums, so we need to validate this ourselves.
 84
+    package_status = config.get('package_status')
 85
+    if package_status not in ('hold', 'install'):
 86
+        charms.apt.status_set('blocked',
 87
+                              'Unknown package_status {}'
 88
+                              ''.format(package_status))
 89
+        # Die before further hooks are run. This isn't very nice, but
 90
+        # there is no other way to inform the operator that they have
 91
+        # invalid configuration.
 92
+        raise SystemExit(0)
 93
+
 94
+    sources = config.get('install_sources')
 95
+    keys = config.get('install_keys')
 96
+    if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)):
 97
+        fetch.configure_sources(update=False,
 98
+                                sources_var='install_sources',
 99
+                                keys_var='install_keys')
100
+        reactive.set_state('apt.needs_update')
101
+
102
+    extra_packages = sorted(config.get('extra_packages', '').split())
103
+    if extra_packages:
104
+        charms.apt.queue_install(extra_packages)
105
+
106
+
107
+def queue_layer_packages():
108
+    """Add packages listed in build-time layer options."""
109
+    # Both basic and apt layer. basic layer will have already installed
110
+    # its defined packages, but rescheduling it here gets the apt layer
111
+    # state set and they will pinned as any other apt layer installed
112
+    # package.
113
+    opts = layer.options()
114
+    for section in ['basic', 'apt']:
115
+        if section in opts and 'packages' in opts[section]:
116
+            charms.apt.queue_install(opts[section]['packages'])
117
+
118
+
119
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
120
+# this module may be imported multiple times so ensure the
121
+# initialization hook is only registered once. I have to piggy back
122
+# onto the namespace of a module imported before reactive discovery
123
+# to do this.
124
+if not hasattr(reactive, '_apt_registered'):
125
+    # We need to register this to run every hook, not just during install
126
+    # and config-changed, to protect against race conditions. If we don't
127
+    # do this, then the config in the hook environment may show updates
128
+    # to running hooks well before the config-changed hook has been invoked
129
+    # and the intialization provided an opertunity to be run.
130
+    hookenv.atstart(hookenv.log, 'Initializing Apt Layer')
131
+    hookenv.atstart(clear_removed_package_states)
132
+    hookenv.atstart(configure_sources)
133
+    hookenv.atstart(queue_layer_packages)
134
+    reactive._apt_registered = True
Back to file index

deps/layer/layer-basic/.gitignore

1
--- 
2
+++ deps/layer/layer-basic/.gitignore
3
@@ -0,0 +1,5 @@
4
+*.pyc
5
+*~
6
+.ropeproject
7
+.settings
8
+.tox
Back to file index

deps/layer/layer-basic/Makefile

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/README.md

  1
--- 
  2
+++ deps/layer/layer-basic/README.md
  3
@@ -0,0 +1,221 @@
  4
+# Overview
  5
+
  6
+This is the base layer for all charms [built using layers][building].  It
  7
+provides all of the standard Juju hooks and runs the
  8
+[charms.reactive.main][charms.reactive] loop for them.  It also bootstraps the
  9
+[charm-helpers][] and [charms.reactive][] libraries and all of their
 10
+dependencies for use by the charm.
 11
+
 12
+# Usage
 13
+
 14
+To create a charm layer using this base layer, you need only include it in
 15
+a `layer.yaml` file:
 16
+
 17
+```yaml
 18
+includes: ['layer:basic']
 19
+```
 20
+
 21
+This will fetch this layer from [interfaces.juju.solutions][] and incorporate
 22
+it into your charm layer.  You can then add handlers under the `reactive/`
 23
+directory.  Note that **any** file under `reactive/` will be expected to
 24
+contain handlers, whether as Python decorated functions or [executables][non-python]
 25
+using the [external handler protocol][].
 26
+
 27
+### Charm Dependencies
 28
+
 29
+Each layer can include a `wheelhouse.txt` file with Python requirement lines.
 30
+For example, this layer's `wheelhouse.txt` includes:
 31
+
 32
+```
 33
+pip>=7.0.0,<8.0.0
 34
+charmhelpers>=0.4.0,<1.0.0
 35
+charms.reactive>=0.1.0,<2.0.0
 36
+```
 37
+
 38
+All of these dependencies from each layer will be fetched (and updated) at build
 39
+time and will be automatically installed by this base layer before any reactive
 40
+handlers are run.
 41
+
 42
+Note that the `wheelhouse.txt` file is intended for **charm** dependencies only.
 43
+That is, for libraries that the charm code itself needs to do its job of deploying
 44
+and configuring the payload.  If the payload itself has Python dependencies, those
 45
+should be handled separately, by the charm.
 46
+
 47
+See [PyPI][pypi charms.X] for packages under the `charms.` namespace which might
 48
+be useful for your charm.
 49
+
 50
+### Layer Namespace
 51
+
 52
+Each layer has a reserved section in the `charms.layer.` Python package namespace,
 53
+which it can populate by including a `lib/charms/layer/<layer-name>.py` file or
 54
+by placing files under `lib/charms/layer/<layer-name>/`.  (If the layer name
 55
+includes hyphens, replace them with underscores.)  These can be helpers that the
 56
+layer uses internally, or it can expose classes or functions to be used by other
 57
+layers to interact with that layer.
 58
+
 59
+For example, a layer named `foo` could include a `lib/charms/layer/foo.py` file
 60
+with some helper functions that other layers could access using:
 61
+
 62
+```python
 63
+from charms.layer.foo import my_helper
 64
+```
 65
+
 66
+### Layer Options
 67
+
 68
+Any layer can define options in its `layer.yaml`.  Those options can then be set
 69
+by other layers to change the behavior of your layer.  The options are defined
 70
+using [jsonschema][], which is the same way that [action paramters][] are defined.
 71
+
 72
+For example, the `foo` layer could include the following option definitons:
 73
+
 74
+```yaml
 75
+includes: ['layer:basic']
 76
+defines:  # define some options for this layer (the layer "foo")
 77
+  enable-bar:  # define an "enable-bar" option for this layer
 78
+    description: If true, enable support for "bar".
 79
+    type: boolean
 80
+    default: false
 81
+```
 82
+
 83
+A layer using `foo` could then set it:
 84
+
 85
+```yaml
 86
+includes: ['layer:foo']
 87
+options:
 88
+  foo:  # setting options for the "foo" layer
 89
+    enable-bar: true  # set the "enable-bar" option to true
 90
+```
 91
+
 92
+The `foo` layer can then use the `charms.layer.options` helper to load the values
 93
+for the options that it defined.  For example:
 94
+
 95
+```python
 96
+from charms import layer
 97
+
 98
+@when('state')
 99
+def do_thing():
100
+  layer_opts = layer.options('foo')  # load all of the options for the "foo" layer
101
+  if layer_opts['enable-bar']:  # check the value of the "enable-bar" option
102
+      hookenv.log("Bar is enabled")
103
+```
104
+
105
+You can also access layer options in other handlers, such as Bash, using
106
+the command-line interface:
107
+
108
+```bash
109
+. charms.reactive.sh
110
+
111
+@when 'state'
112
+function do_thing() {
113
+    if layer_option foo enable-bar; then
114
+        juju-log "Bar is enabled"
115
+        juju-log "bar-value is: $(layer_option foo bar-value)"
116
+    fi
117
+}
118
+
119
+reactive_handler_main
120
+```
121
+
122
+Note that options of type `boolean` will set the exit code, while other types
123
+will be printed out.
124
+
125
+# Hooks
126
+
127
+This layer provides hooks that other layers can react to using the decorators
128
+of the [charms.reactive][] library:
129
+
130
+  * `config-changed`
131
+  * `install`
132
+  * `leader-elected`
133
+  * `leader-settings-changed`
134
+  * `start`
135
+  * `stop`
136
+  * `upgrade-charm`
137
+  * `update-status`
138
+
139
+Other hooks are not implemented at this time. A new layer can implement storage
140
+or relation hooks in their own layer by putting them in the `hooks` directory.
141
+
142
+**Note:** Because `update-status` is invoked every 5 minutes, you should take
143
+care to ensure that your reactive handlers only invoke expensive operations
144
+when absolutely necessary.  It is recommended that you use helpers like
145
+[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure
146
+that handlers run only when necessary.
147
+
148
+# Layer Configuration
149
+
150
+This layer supports the following options, which can be set in `layer.yaml`:
151
+
152
+  * **packages**  A list of system packages to be installed before the reactive
153
+    handlers are invoked.
154
+
155
+  * **use_venv**  If set to true, the charm dependencies from the various
156
+    layers' `wheelhouse.txt` files will be installed in a Python virtualenv
157
+    located at `$CHARM_DIR/../.venv`.  This keeps charm dependencies from
158
+    conflicting with payload dependencies, but you must take care to preserve
159
+    the environment and interpreter if using `execl` or `subprocess`.
160
+
161
+  * **include_system_packages**  If set to true and using a venv, include
162
+    the `--system-site-packages` options to make system Python libraries
163
+    visible within the venv.
164
+
165
+An example `layer.yaml` using these options might be:
166
+
167
+```yaml
168
+includes: ['layer:basic']
169
+options:
170
+  basic:
171
+    packages: ['git']
172
+    use_venv: true
173
+    include_system_packages: true
174
+```
175
+
176
+
177
+# Reactive States
178
+
179
+This layer will set the following states:
180
+
181
+  * **`config.changed`**  Any config option has changed from its previous value.
182
+    This state is cleared automatically at the end of each hook invocation.
183
+
184
+  * **`config.changed.<option>`** A specific config option has changed.
185
+    **`<option>`** will be replaced by the config option name from `config.yaml`.
186
+    This state is cleared automatically at the end of each hook invocation.
187
+
188
+  * **`config.set.<option>`** A specific config option has a True or non-empty
189
+    value set.  **`<option>`** will be replaced by the config option name from
190
+    `config.yaml`.  This state is cleared automatically at the end of each hook
191
+    invocation.
192
+
193
+  * **`config.default.<option>`** A specific config option is set to its
194
+    default value.  **`<option>`** will be replaced by the config option name
195
+    from `config.yaml`.  This state is cleared automatically at the end of
196
+    each hook invocation.
197
+
198
+An example using the config states would be:
199
+
200
+```python
201
+@when('config.changed.my-opt')
202
+def my_opt_changed():
203
+    update_config()
204
+    restart_service()
205
+```
206
+
207
+
208
+# Actions
209
+
210
+This layer currently does not define any actions.
211
+
212
+
213
+[building]: https://jujucharms.com/docs/devel/authors-charm-building
214
+[charm-helpers]: https://pythonhosted.org/charmhelpers/
215
+[charms.reactive]: https://pythonhosted.org/charms.reactive/
216
+[interfaces.juju.solutions]: http://interfaces.juju.solutions/
217
+[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers
218
+[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler
219
+[jsonschema]: http://json-schema.org/
220
+[action paramters]: https://jujucharms.com/docs/stable/authors-charm-actions
221
+[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search
222
+[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once
223
+[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed
224
+[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed
Back to file index

deps/layer/layer-basic/bin/layer_option

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/copyright

 1
--- 
 2
+++ deps/layer/layer-basic/copyright
 3
@@ -0,0 +1,9 @@
 4
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0
 5
+
 6
+Files: *
 7
+Copyright: 2015, Canonical Ltd.
 8
+License: GPL-3
 9
+
10
+License: GPL-3
11
+ On Debian GNU/Linux system you can find the complete text of the
12
+ GPL-3 license in '/usr/share/common-licenses/GPL-3'
Back to file index

deps/layer/layer-basic/hooks/config-changed

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/hook.template

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/install

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/leader-elected

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/leader-settings-changed

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/start

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/stop

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/update-status

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/hooks/upgrade-charm

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-basic/layer.yaml
 3
@@ -0,0 +1,18 @@
 4
+defines:
 5
+  packages:
 6
+    type: array
 7
+    default: []
 8
+    description: Additional packages to be installed at time of bootstrap
 9
+  use_venv:
10
+    type: boolean
11
+    default: false
12
+    description: >
13
+      Install charm dependencies (wheelhouse) into a Python virtual environment
14
+      to help avoid conflicts with other charms or libraries on the machine.
15
+  include_system_packages:
16
+    type: boolean
17
+    default: false
18
+    description: >
19
+      If using a virtual environment, allow the venv to see Python packages
20
+      installed at the system level.  This reduces isolation, but is necessary
21
+      to use Python packages installed via apt-get.
Back to file index

deps/layer/layer-basic/lib/charms/layer/__init__.py

 1
--- 
 2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/lib/charms/layer/basic.py

  1
--- 
  2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/lib/charms/layer/execd.py

  1
--- 
  2
+++ deps/layer/layer-basic/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

deps/layer/layer-basic/metadata.yaml

1
--- 
2
+++ deps/layer/layer-basic/metadata.yaml
3
@@ -0,0 +1 @@
4
+{}
Back to file index

deps/layer/layer-basic/requirements.txt

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

deps/layer/layer-basic/tox.ini

 1
--- 
 2
+++ deps/layer/layer-basic/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
Back to file index

deps/layer/layer-basic/wheelhouse.txt

1
--- 
2
+++ deps/layer/layer-basic/wheelhouse.txt
3
@@ -0,0 +1,3 @@
4
+pip>=7.0.0,<8.2.0
5
+charmhelpers>=0.4.0,<1.0.0
6
+charms.reactive>=0.1.0,<2.0.0
Back to file index

deps/layer/layer-leadership/.gitignore

1
--- 
2
+++ deps/layer/layer-leadership/.gitignore
3
@@ -0,0 +1,2 @@
4
+*~
5
+*.pyc
Back to file index

deps/layer/layer-leadership/README.md

 1
--- 
 2
+++ deps/layer/layer-leadership/README.md
 3
@@ -0,0 +1,88 @@
 4
+# Leadership Layer for Juju Charms
 5
+
 6
+The Leadership layer is for charm-tools and 'charm build', making it
 7
+easier for layered charms to deal with Juju leadership.
 8
+
 9
+This layer will initialize charms.reactive states, allowing you to
10
+write handlers that will be activated by these states. It allows you
11
+to completely avoid writing leader-elected and leader-settings-changed
12
+hooks. As a simple example, these two handlers are all that is required
13
+to make the leader unit generate a password if it is not already set,
14
+and have the shared password stored in a file on all units:
15
+
16
+```python
17
+import charms.leadership
18
+from charmhelpers.core.host import pwgen
19
+
20
+
21
+@when('leadership.is_leader')
22
+@when_not('leadership.set.admin_password')
23
+def generate_secret():
24
+    charms.leadership.leader_set(admin_password=pwgen())
25
+
26
+
27
+@when('leadership.changed.admin_password')
28
+def store_secret():
29
+    write_file('/etc/foopass', leader_get('admin_password'))
30
+```
31
+
32
+
33
+## States
34
+
35
+The following states are set appropriately on startup, before any @hook
36
+decorated methods are invoked:
37
+
38
+* `leadership.is_leader`
39
+
40
+  This state is set when the unit is the leader. The unit will remain
41
+  the leader for the remainder of the hook, but may not be leader in
42
+  future hooks.
43
+
44
+* `leadership.set.{varname}`
45
+
46
+  This state is set for each leadership setting (ie. the
47
+  `leadership.set.foo` state will be set if the leader has set
48
+  the foo leadership setting to any value). It will remain
49
+  set for the remainder of the hook, unless the unit is the leader
50
+  and calls `reactive.leadership.leader_set()` and resets the value
51
+  to None.
52
+
53
+* `leadership.changed.{varname}`
54
+
55
+  This state is set for each leadership setting that has changed
56
+  since the last hook. It will remain set for the remainder of the
57
+  hook. It will not be set in the next hook, unless the leader has
58
+  changed the leadership setting yet again.
59
+
60
+* `leadership.changed`
61
+
62
+  One or more leadership settings has changed since the last hook.
63
+  This state will remain set for the remainder of the hook. It will
64
+  not be set in the next hook, unless the leader has made further
65
+  changes.
66
+
67
+
68
+## Methods
69
+
70
+The `charms.leadership` module exposes the `leader_set()` and
71
+`leader_get()` methods, which match the methods found in the
72
+`charmhelpers.core.hookenv` module. `reactive.leadership.leader_set()`
73
+should be used instead of the charmhelpers function to ensure that
74
+the reactive state is updated when the leadership settings are. If you
75
+do not do this, then you risk handlers waiting on these states to not
76
+be run on the leader (because when the leader changes settings, it 
77
+triggers leader-settings-changed hooks on the follower units but
78
+no hooks on itself).
79
+
80
+
81
+## Support
82
+
83
+This layer is maintained on Launchpad by
84
+Stuart Bishop (stuart.bishop@canonical.com).
85
+
86
+Code is available using git at git+ssh://git.launchpad.net/layer-leadership.
87
+
88
+Bug reports can be made at https://bugs.launchpad.net/layer-leadership.
89
+
90
+Queries and comments can be made on the Juju mailing list, Juju IRC
91
+channels, or at https://answers.launchpad.net/layer-leadership.
Back to file index

deps/layer/layer-leadership/copyright

 1
--- 
 2
+++ deps/layer/layer-leadership/copyright
 3
@@ -0,0 +1,15 @@
 4
+Copyright 2015-2016 Canonical Ltd.
 5
+
 6
+This file is part of the Leadership Layer for Juju.
 7
+
 8
+This program is free software: you can redistribute it and/or modify
 9
+it under the terms of the GNU General Public License version 3, as
10
+published by the Free Software Foundation.
11
+
12
+This program is distributed in the hope that it will be useful, but
13
+WITHOUT ANY WARRANTY; without even the implied warranties of
14
+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+PURPOSE.  See the GNU General Public License for more details.
16
+
17
+You should have received a copy of the GNU General Public License
18
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
Back to file index

deps/layer/layer-leadership/layer.yaml

 1
--- 
 2
+++ deps/layer/layer-leadership/layer.yaml
 3
@@ -0,0 +1,17 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+includes:
20
+    - layer:basic
Back to file index

deps/layer/layer-leadership/lib/charms/__init__.py

1
--- 
2
+++ deps/layer/layer-leadership/lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

deps/layer/layer-leadership/lib/charms/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/lib/charms/leadership.py
 3
@@ -0,0 +1,58 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.reactive import not_unless
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']
28
+
29
+
30
+@not_unless('leadership.is_leader')
31
+def leader_set(settings=None, **kw):
32
+    '''Change leadership settings, per charmhelpers.core.hookenv.leader_set.
33
+
34
+    The leadership.set.{key} reactive state will be set while the
35
+    leadership hook environment setting remains set.
36
+
37
+    Changed leadership settings will set the leadership.changed.{key}
38
+    and leadership.changed states. These states will remain set until
39
+    the following hook.
40
+
41
+    These state changes take effect immediately on the leader, and
42
+    in future hooks run on non-leaders. In this way both leaders and
43
+    non-leaders can share handlers, waiting on these states.
44
+    '''
45
+    settings = settings or {}
46
+    settings.update(kw)
47
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
48
+
49
+    for key, value in settings.items():
50
+        if value != previous.get(key):
51
+            reactive.set_state('leadership.changed.{}'.format(key))
52
+            reactive.set_state('leadership.changed')
53
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
54
+                                      value is not None)
55
+    hookenv.leader_set(settings)
56
+    unitdata.kv().update(settings, prefix='leadership.settings.')
57
+
58
+
59
+def leader_get(attribute=None):
60
+    '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.'''
61
+    return hookenv.leader_get(attribute)
Back to file index

deps/layer/layer-leadership/reactive/leadership.py

 1
--- 
 2
+++ deps/layer/layer-leadership/reactive/leadership.py
 3
@@ -0,0 +1,68 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.leadership import leader_get, leader_set
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']  # Backwards compatibility
28
+
29
+
30
+def initialize_leadership_state():
31
+    '''Initialize leadership.* states from the hook environment.
32
+
33
+    Invoked by hookenv.atstart() so states are available in
34
+    @hook decorated handlers.
35
+    '''
36
+    is_leader = hookenv.is_leader()
37
+    if is_leader:
38
+        hookenv.log('Initializing Leadership Layer (is leader)')
39
+    else:
40
+        hookenv.log('Initializing Leadership Layer (is follower)')
41
+
42
+    reactive.helpers.toggle_state('leadership.is_leader', is_leader)
43
+
44
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
45
+    current = hookenv.leader_get()
46
+
47
+    # Handle deletions.
48
+    for key in set(previous.keys()) - set(current.keys()):
49
+        current[key] = None
50
+
51
+    any_changed = False
52
+    for key, value in current.items():
53
+        reactive.helpers.toggle_state('leadership.changed.{}'.format(key),
54
+                                      value != previous.get(key))
55
+        if value != previous.get(key):
56
+            any_changed = True
57
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
58
+                                      value is not None)
59
+    reactive.helpers.toggle_state('leadership.changed', any_changed)
60
+
61
+    unitdata.kv().update(current, prefix='leadership.settings.')
62
+
63
+
64
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
65
+# this module may be imported multiple times so ensure the
66
+# initialization hook is only registered once. I have to piggy back
67
+# onto the namespace of a module imported before reactive discovery
68
+# to do this.
69
+if not hasattr(reactive, '_leadership_registered'):
70
+    hookenv.atstart(initialize_leadership_state)
71
+    reactive._leadership_registered = True
Back to file index

deps/layer/trunk/README.md

 1
--- 
 2
+++ deps/layer/trunk/README.md
 3
@@ -0,0 +1,57 @@
 4
+# IBM base layer
 5
+
 6
+The IBM base layer is a common starting point for IBM related charms. Use this
 7
+layer if you are building a layered charm with IBM software.
 8
+
 9
+## Usage
10
+
11
+This charm is a base layer and not meant to be built as a charm on its own.
12
+
13
+To use this layer, simply include the following in the runtime charm's
14
+layer.yaml file
15
+
16
+```yaml
17
+includes: ['layer:ibm-base']
18
+```
19
+
20
+## Configuration
21
+
22
+**curl_url** - Location of the IBM product installation file(s). This should be a
23
+URL that curl can use to download files. Multiple URLs should be separated
24
+by a space.
25
+
26
+**NOTE**: cryptographic verification is required and must be
27
+specified as part of the URL query string with the key a valid hash
28
+algorithms md5, sha256, or sha512, and the the checksum value itself
29
+(`http://<url>?[md5|sha256|sha512]=<checksum>`).
30
+
31
+For example:  
32
+
33
+* `http://example.com/file.tgz?sha256=<sum>`
34
+* `"sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>"`
35
+
36
+**curl_options** - The options passed to the 'curl' command when fetching files
37
+from curl_url.
38
+
39
+For example:  
40
+
41
+* `"-u <user:password>"`
42
+
43
+**license_accepted** - Some IBM charms require acceptance of a license before
44
+installation can proceed. If required, setting this option to True indicates
45
+that you have read and accepted the IBM terms and conditions found in the
46
+license file referenced by the charm.
47
+
48
+## States
49
+
50
+**ibm-base.curl.resource.fetched** - When this state is set the IBM base layer has
51
+downloaded and verified the resources configured by curl_url. If the charm
52
+does not require any curl resources this state can be ignored.
53
+
54
+**ibm-base.license.accepted** - When this state is set the user has signified
55
+their acceptance of the license found in the charm. If the charm implements
56
+the Juju 'terms' feature this state can be safely ignored.
57
+
58
+## Contacts
59
+
60
+IBM Juju Support Team <jujusupp@us.ibm.com>
Back to file index

deps/layer/trunk/config.yaml

 1
--- 
 2
+++ deps/layer/trunk/config.yaml
 3
@@ -0,0 +1,29 @@
 4
+options:
 5
+  curl_url:
 6
+    type: string
 7
+    default: ''
 8
+    description: |
 9
+      Location of the IBM product installation file(s). This should be a URL
10
+      that curl can use to download files. Multiple URLs should be separated
11
+      by a space. NOTE: cryptographic verification is required and must be
12
+      specified as part of the URL query string with the key a valid hash
13
+      algorithms md5, sha256, or sha512, and the the checksum value itself
14
+      (http://<url>?[md5|sha256|sha512]=<checksum>).
15
+      For example:
16
+        'http://example.com/file.tgz?sha256=<sum>'
17
+        'sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>'
18
+  curl_opts:
19
+    type: string
20
+    default: ''
21
+    description: |
22
+      The options passed to the 'curl' command when fetching files from
23
+      curl_url. For example:
24
+        '-u <user:password>'
25
+  license_accepted:
26
+    type: boolean
27
+    default: False
28
+    description: |
29
+      Some IBM charms require acceptance of a license before installation
30
+      can proceed. If required, setting this option to True indicates that you
31
+      have read and accepted the IBM terms and conditions found in the license
32
+      file referenced by the charm.
Back to file index

deps/layer/trunk/layer.yaml

 1
--- 
 2
+++ deps/layer/trunk/layer.yaml
 3
@@ -0,0 +1,9 @@
 4
+includes:
 5
+  - 'layer:basic'
 6
+  - 'layer:apt'
 7
+  - 'layer:leadership'
 8
+options:
 9
+  basic:
10
+    # Setting options for the basic layer.
11
+    packages:
12
+      - curl
Back to file index

deps/layer/trunk/metadata.yaml

 1
--- 
 2
+++ deps/layer/trunk/metadata.yaml
 3
@@ -0,0 +1,10 @@
 4
+name: ibm-base
 5
+summary: This layer provides a common base for IBM charms to build off of.
 6
+maintainer: IBM Juju Support Team <jujusupp@us.ibm.com>
 7
+description: |
 8
+  This layer provides apt support from the apt layer and Juju leadership from
 9
+  the leadership layer. It also provides a curl mechanism and license controls
10
+  for IBM charms that do not yet support juju resources and terms.
11
+min-juju-version: '2.0-beta1'
12
+tags:
13
+  - ibm
Back to file index

deps/layer/trunk/reactive/ibm-base.sh

  1
--- 
  2
+++ deps/layer/trunk/reactive/ibm-base.sh
  3
@@ -0,0 +1,107 @@
  4
+#!/bin/bash
  5
+source charms.reactive.sh
  6
+set -e
  7
+
  8
+
  9
+# Utility function to verify a downloaded resource
 10
+# :param: file name
 11
+# :param: checksum type
 12
+# :param: checksum value
 13
+function verify_curl_resource() {
 14
+  local FILE=$1
 15
+  local TYPE=$2
 16
+  local EXPECTED_SUM=$3
 17
+  local CALCULATED_SUM=""
 18
+  local PROG=""
 19
+
 20
+  if [ ! -r ${FILE} ]; then
 21
+    status-set blocked "ibm-base: could not read ${FILE}"
 22
+    juju-log "Could not verify the downloaded resource. File could not be read: ${FILE}"
 23
+  fi
 24
+
 25
+  # Set our checksum utility based on the requested type
 26
+  case "${TYPE}" in
 27
+    md5)
 28
+      PROG='md5sum'
 29
+      ;;
 30
+    sha256)
 31
+      PROG='sha256sum'
 32
+      ;;
 33
+    sha512)
 34
+      PROG='sha512sum'
 35
+      ;;
 36
+    *)
 37
+      status-set blocked "ibm-base: checksum type must be md5, sha215, or sha512"
 38
+      juju-log "Could not verify the downloaded resource ${FILE}. Unknown checksum type: ${TYPE}"
 39
+      return 1
 40
+  esac
 41
+
 42
+  CALCULATED_SUM=`${PROG} ${FILE} | awk '{print $1}'`
 43
+  if [ "${CALCULATED_SUM}" = "${EXPECTED_SUM}" ]; then
 44
+    juju-log "Checksum verified for ${FILE}."
 45
+    return 0
 46
+  else
 47
+    status-set blocked "ibm-base: checksums did not match"
 48
+    juju-log "Checksum mismatch for ${FILE}. Expected ${EXPECTED_SUM}, got ${CALCULATED_SUM}"
 49
+    return 1
 50
+  fi
 51
+}
 52
+
 53
+
 54
+# Fetch curl resources if a URL is configured
 55
+@when 'config.set.curl_url'
 56
+@when_any 'config.new.curl_url' 'config.changed.curl_url' 'config.new.curl_opts' 'config.changed.curl_opts'
 57
+function fetch_curl_resource() {
 58
+  local ARCHIVE_DIR="${CHARM_DIR}/files/archives"
 59
+  local CURL_URL=$(config-get 'curl_url')
 60
+  local CURL_OPTS=$(config-get 'curl_opts')
 61
+
 62
+  status-set maintenance "ibm-base: fetching resource(s)"
 63
+
 64
+  mkdir -p ${ARCHIVE_DIR}
 65
+  cd ${ARCHIVE_DIR}
 66
+  # Multiple URLs may be separated by a space, so loop.
 67
+  for URL_STRING in ${CURL_URL}
 68
+  do
 69
+    # For each URL_STRING, set the url, checksum type, and checksum value.
 70
+    local URL=${URL_STRING%%\?*}        # string before the first '?'
 71
+    local FILE_NAME=${URL##*\/}         # string after the last '/'
 72
+    local SUM_STRING=${URL_STRING#*\?}  # string after the first '?'
 73
+    local SUM_TYPE=${SUM_STRING%%\=*}   # string before the first '='
 74
+    local SUM_VALUE=${SUM_STRING#*\=}   # string after the first '='
 75
+
 76
+    if [ -z ${FILE_NAME} ]; then
 77
+      FILE_NAME="juju-${RANDOM}"
 78
+    fi
 79
+    curl --silent --show-error ${CURL_OPTS} -o ${FILE_NAME} ${URL}
 80
+
 81
+    # Verify our resource checksum. If this fails, let verify_resource log
 82
+    # the reason and exit successfully. Exiting non-zero would fail the hook,
 83
+    # so return 0 and simply inform the user that verification failed.
 84
+    verify_curl_resource ${FILE_NAME} ${SUM_TYPE} ${SUM_VALUE} || return 0
 85
+  done
 86
+  cd -
 87
+
 88
+  set_state 'ibm-base.curl.resource.fetched'
 89
+  status-set active "ibm-base: curl resource(s) fetched"
 90
+  juju-log 'Curl resource fetched'
 91
+}
 92
+
 93
+
 94
+# Handle license acceptance
 95
+@when 'config.changed.license_accepted'
 96
+function check_license_acceptance() {
 97
+  local LIC_ACCEPTED=$(config-get 'license_accepted')
 98
+
 99
+  # compare lowercase LIC_ACCEPTED (requires bash > 4)
100
+  if [ "${LIC_ACCEPTED,,}" = "true" ]; then
101
+    set_state 'ibm-base.license.accepted'
102
+    juju-log 'License accepted'
103
+  else
104
+    remove_state 'ibm-base.license.accepted'
105
+    juju-log 'License NOT accepted'
106
+  fi
107
+}
108
+
109
+# Main reactive entry point
110
+reactive_handler_main
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/dm-node-relation-broken

 1
--- 
 2
+++ hooks/dm-node-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/dm-node-relation-changed

 1
--- 
 2
+++ hooks/dm-node-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/dm-node-relation-departed

 1
--- 
 2
+++ hooks/dm-node-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/dm-node-relation-joined

 1
--- 
 2
+++ hooks/dm-node-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/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/dm-node/@

 1
--- 
 2
+++ hooks/relations/dm-node/@
 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/dm-node/README.md

 1
--- 
 2
+++ hooks/relations/dm-node/README.md
 3
@@ -0,0 +1,38 @@
 4
+Overview
 5
+-----------
 6
+
 7
+This interface layer handles the communication between a `IBM WAS ND DM` and `IBM WAS ND Node` charms.
 8
+The provider side of this interface provides the WAS ND DM service. The consumer/Requires part will be the WAS ND Node which wants the WAS ND DM dmgruser, dmgrpw, hostname, dminstallpath and profile name to configure the WAS ND dynamic cluster.
 9
+
10
+
11
+Usage
12
+------
13
+
14
+#### Provides
15
+IBM WebSphere Application Server ND DM will provide this interface. This interface layer will set the following states, as appropriate:
16
+
17
+ - `{relation_name}.connected`: The relation is established, WAS ND DM service is ready to send it's information.
18
+ 
19
+ - `{relation_name}.available`: WAS ND DM has provided its connection information, and is ready to accept requests from the WAS ND Node charm.
20
+ 
21
+ - `{relation_name}.departed` : The relation has been removed. Any cleanup related to the consumer charm(WAS ND Node) should happen now on the WAS ND DM charm since the consumer is going away.
22
+
23
+
24
+#### Requires
25
+
26
+Consumer charm `IBM WAS ND Node` will require this interface to connect to WAS ND DM so that they can share information to configure dynamic cluster setup. This interface layer will set the following states, as appropriate:
27
+
28
+- `{relation_name}.connected` : The consumer charm has been related to a WAS ND DM provider charm. At this point, the charm waits for Provider charm to send configuration details like dmgruser, dmgrpw, hostname, dminstallpath and profile name.
29
+
30
+- `{relation_name}.ready` : WAS ND Node is now ready to access the configuration details provided by WAS ND DM charm. The WAS ND Node charm can access the configuration details using the below methods:
31
+
32
+    - `get_was_dm_install_path()` - returns the Installation path of WAS ND DM.
33
+    - `get_was_dm_hostname()` - returns host name of WAS ND DM.
34
+    - `get_dmgradminuser()` - returns user name to login DM console.
35
+    - `get_dmgrpwd()` - returns password to login DM console.
36
+    - `get_was_dm_profile_name()` - returns the WAS ND DM profile name.
37
+	
38
+Once the relation established, send the WAS ND Node host name to WAS ND DM. 
39
+    - `set_was_node_hostname()` - returns running port of WAS.
40
+
41
+
Back to file index

hooks/relations/dm-node/interface.yaml

1
--- 
2
+++ hooks/relations/dm-node/interface.yaml
3
@@ -0,0 +1,5 @@
4
+name: dm-node 
5
+summary: WAS ND provides the relation to IBM WAS ND Node charm
6
+maintainer: IBM Juju Support Team <jujusupp@us.ibm.com>
7
+version: 1
8
+#repo: ""
Back to file index

hooks/relations/dm-node/provides.py

 1
--- 
 2
+++ hooks/relations/dm-node/provides.py
 3
@@ -0,0 +1,61 @@
 4
+from charms.reactive import hook
 5
+from charms.reactive import RelationBase
 6
+from charms.reactive import scopes
 7
+
 8
+
 9
+class wasdmProvides(RelationBase):
10
+    # Every unit connecting will get the same information
11
+    scope = scopes.UNIT
12
+
13
+    @hook('{provides:dm-node}-relation-{joined}')
14
+    def joined(self):
15
+        conversation = self.conversation()
16
+        conversation.set_state('{relation_name}.connected')
17
+        conversation.remove_state('{relation_name}.departed')
18
+
19
+    @hook('{provides:dm-node}-relation-changed')
20
+    def changed(self):
21
+        conversation = self.conversation()
22
+        conversation.set_state('{relation_name}.available')
23
+
24
+    @hook('{provides:dm-node}-relation-{broken}')
25
+    def broken(self):
26
+        conversation = self.conversation()
27
+        conversation.remove_state('{relation_name}.connected')
28
+        conversation.remove_state('{relation_name}.available')
29
+        conversation.set_state('{relation_name}.departed')
30
+
31
+    def dismiss(self):
32
+        for conv in self.conversations():
33
+            conv.remove_state('{relation_name}.departed')
34
+
35
+    def reset_states(self, service):
36
+        conversation = self.conversation(service)
37
+        conversation.remove_state('{relation_name}.connected')
38
+        conversation.remove_state('{relation_name}.departed')
39
+
40
+    def set_was_dm_details(self, hostname, dminstallpath,
41
+                           profilename, dmgruser, dmgrpwd):
42
+        for conversation in self.conversations():
43
+            conversation.set_remote(data={
44
+                                          'hostname': hostname,
45
+                                          'dminstallpath': dminstallpath,
46
+                                          'profilename': profilename,
47
+                                          'dmgruser': dmgruser,
48
+                                          'dmgrpwd': dmgrpwd})
49
+
50
+    def get_was_node_ip(self):
51
+        ip = []
52
+        for conv in self.conversations():
53
+            ip.append(conv.get_remote('private-address') +
54
+                      " " + (conv.get_remote('node_hostname')))
55
+        return ip
56
+
57
+    def services(self):
58
+        """
59
+        Return a list of services connecting to WASND.
60
+        """
61
+        service = []
62
+        for conversation in self.conversations():
63
+            service.append(conversation.scope)
64
+        return service
Back to file index

hooks/relations/dm-node/requires.py

 1
--- 
 2
+++ hooks/relations/dm-node/requires.py
 3
@@ -0,0 +1,49 @@
 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 = ['hostname', 'dmgruser', 'dmgrpwd']
13
+
14
+    @hook('{requires:dm-node}-relation-joined')
15
+    def joined(self):
16
+        self.remove_state('{relation_name}.departed')
17
+        self.set_state('{relation_name}.connected')
18
+
19
+    @hook('{requires:dm-node}-relation-changed')
20
+    def changed(self):
21
+        self.remove_state('{relation_name}.departed')
22
+        if str(self.get_remote('hostname')) != "None":
23
+            self.set_state('{relation_name}.ready')
24
+
25
+    @hook('{requires:dm-node}-relation-departed')
26
+    def departed(self):
27
+        self.remove_state('{relation_name}.connected')
28
+        self.remove_state('{relation_name}.ready')
29
+        self.set_state('{relation_name}.departed')
30
+
31
+    def get_was_dm_hostname(self):
32
+        return self.get_remote('hostname')
33
+
34
+    def get_was_dm_ip(self):
35
+        return self.get_remote('private-address')
36
+
37
+    def get_was_dm_install_path(self):
38
+        return self.get_remote('dminstallpath')
39
+
40
+    def get_was_dm_profile_name(self):
41
+        return self.get_remote('profilename')
42
+
43
+    def get_dmgradminuser(self):
44
+        return self.get_remote('dmgruser')
45
+
46
+    def get_dmgrpwd(self):
47
+        return self.get_remote('dmgrpwd')
48
+
49
+    def set_was_node_hostname(self, node_hostname):
50
+        for conversation in self.conversations():
51
+            conversation.set_remote(data={
52
+                                          'node_hostname': node_hostname})
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/relations/was-nd/@

 1
--- 
 2
+++ hooks/relations/was-nd/@
 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-nd/README.md

 1
--- 
 2
+++ hooks/relations/was-nd/README.md
 3
@@ -0,0 +1,34 @@
 4
+Overview
 5
+-----------
 6
+
 7
+This interface layer handles the communication between a `IBM WAS ND DM` and Consumer charms (ex: WXS) charms.
 8
+The provider side of this interface provides the WAS ND DM service. The consumer which wants the WAS ND DM dmgruser, dmgrpw, hostname, dminstallpath and profile name.
 9
+
10
+
11
+Usage
12
+------
13
+
14
+#### Provides
15
+IBM WebSphere Application Server ND DM will provide this interface. This interface layer will set the following states, as appropriate:
16
+
17
+ - `{relation_name}.connected`: The relation is established, WAS ND DM service is ready to send it's information.
18
+ 
19
+ - `{relation_name}.available`: WAS ND DM has provided its connection information, and is ready to accept requests from the consumer charm.
20
+ 
21
+ - `{relation_name}.departed` : The relation has been removed. Any cleanup related to the consumer charm should happen now on the WAS ND DM charm since the consumer is going away.
22
+
23
+
24
+#### Requires
25
+
26
+Consumer charm will require this interface to connect to WAS ND DM so that they can share information to configure dynamic cluster setup. This interface layer will set the following states, as appropriate:
27
+
28
+- `{relation_name}.connected` : The consumer charm has been related to a WAS ND DM provider charm. At this point, the charm waits for Provider charm to send configuration details like dmgruser, dmgrpw, hostname, dminstallpath and profile name.
29
+
30
+- `{relation_name}.ready` : Consumer charm is now ready to access the configuration details provided by WAS ND DM charm. The consumer charm can access the configuration details using the below methods:
31
+
32
+    - `get_was_dm_install_path()` - returns the Installation path of WAS ND DM.
33
+    - `get_was_dm_hostname()` - returns host name of WAS ND DM.
34
+    - `get_dmgradminuser()` - returns user name to login DM console.
35
+    - `get_dmgrpwd()` - returns password to login DM console.
36
+    - `get_was_dm_profile_name()` - returns the WAS ND DM profile name.
37
+	
Back to file index

hooks/relations/was-nd/interface.yaml

1
--- 
2
+++ hooks/relations/was-nd/interface.yaml
3
@@ -0,0 +1,5 @@
4
+name: was-nd
5
+summary: WAS ND provides the relation to consumer charms
6
+maintainer: IBM Juju Support Team <jujusupp@us.ibm.com>
7
+version: 1
8
+#repo: ""
Back to file index

hooks/relations/was-nd/provides.py

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

hooks/relations/was-nd/requires.py

 1
--- 
 2
+++ hooks/relations/was-nd/requires.py
 3
@@ -0,0 +1,41 @@
 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 = ['hostname', 'dmgruser', 'dmgrpwd']
13
+
14
+    @hook('{requires:was-nd}-relation-joined')
15
+    def joined(self):
16
+        self.remove_state('{relation_name}.departed')
17
+        self.set_state('{relation_name}.connected')
18
+
19
+    @hook('{requires:was-nd}-relation-changed')
20
+    def changed(self):
21
+        self.remove_state('{relation_name}.departed')
22
+        if str(self.get_remote('hostname')) != "None":
23
+            self.set_state('{relation_name}.ready')
24
+
25
+    @hook('{requires:was-nd}-relation-departed')
26
+    def departed(self):
27
+        self.remove_state('{relation_name}.connected')
28
+        self.remove_state('{relation_name}.ready')
29
+        self.set_state('{relation_name}.departed')
30
+
31
+    def get_was_dm_hostname(self):
32
+        return self.get_remote('hostname')
33
+
34
+    def get_was_dm_install_path(self):
35
+        return self.get_remote('dminstallpath')
36
+
37
+    def get_was_dm_profile_name(self):
38
+        return self.get_remote('profilename')
39
+
40
+    def get_dmgradminuser(self):
41
+        return self.get_remote('dmgruser')
42
+
43
+    def get_dmgrpwd(self):
44
+        return self.get_remote('dmgrpwd')
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/was-nd-relation-broken

 1
--- 
 2
+++ hooks/was-nd-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-nd-relation-changed

 1
--- 
 2
+++ hooks/was-nd-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-nd-relation-departed

 1
--- 
 2
+++ hooks/was-nd-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-nd-relation-joined

 1
--- 
 2
+++ hooks/was-nd-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

index.html

 1
--- 
 2
+++ index.html
 3
@@ -0,0 +1,7 @@
 4
+<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="en-IN"><head><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/images/branding/googleg/1x/googleg_standard_color_128dp.png" itemprop="image"><title>Google</title><script>(function(){window.google={kEI:'e1q9V5eIEMjivgSjuqD4BQ',kEXPI:'1351587,3700274,3700389,4029815,4031109,4032678,4036509,4036527,4038012,4039268,4041302,4043492,4045841,4048346,4054590,4061155,4061922,4062702,4062706,4063879,4064815,4065787,4065793,4065959,4066195,4066654,4066708,4067175,4067860,4067939,4068550,4069839,4069841,4069904,4070060,4070221,4070455,4070598,4071020,4071229,4071577,4071603,4071842,4072000,4072213,4072288,4072364,4072579,4072624,4072653,4072682,4072776,4073231,4073249,4073405,4073418,4073889,4073958,4073979,4074348,4074559,4074800,4074801,4075122,4075451,4075720,4075782,4075787,4075860,4075966,4075974,4076096,4076116,4076744,4077218,4077221,8300096,8300272,8502184,8503585,8504846,8505150,8505152,8505585,8505677,10200083,16200026',authuser:0,kscs:'c9c918f0_24'};google.kHL='en-IN';})();(function(){google.lc=[];google.li=0;google.getEI=function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute("eid")));)a=a.parentNode;return b||google.kEI};google.getLEI=function(a){for(var b=null;a&&(!a.getAttribute||!(b=a.getAttribute("leid")));)a=a.parentNode;return b};google.https=function(){return"https:"==window.location.protocol};google.ml=function(){return null};google.wl=function(a,b){try{google.ml(Error(a),!1,b)}catch(c){}};google.time=function(){return(new Date).getTime()};google.log=function(a,b,c,e,g){a=google.logUrl(a,b,c,e,g);if(""!=a){b=new Image;var d=google.lc,f=google.li;d[f]=b;b.onerror=b.onload=b.onabort=function(){delete d[f]};window.google&&window.google.vel&&window.google.vel.lu&&window.google.vel.lu(a);b.src=a;google.li=f+1}};google.logUrl=function(a,b,c,e,g){var d="",f=google.ls||"";if(!c&&-1==b.search("&ei=")){var h=google.getEI(e),d="&ei="+h;-1==b.search("&lei=")&&((e=google.getLEI(e))?d+="&lei="+e:h!=google.kEI&&(d+="&lei="+google.kEI))}a=c||"/"+(g||"gen_204")+"?atyp=i&ct="+a+"&cad="+b+d+f+"&zx="+google.time();/^http:/i.test(a)&&google.https()&&(google.ml(Error("a"),!1,{src:a,glmm:1}),a="");return a};google.y={};google.x=function(a,b){google.y[a.id]=[a,b];return!1};google.load=function(a,b,c){google.x({id:a+k++},function(){google.load(a,b,c)})};var k=0;})();var _gjwl=location;function _gjuc(){var a=_gjwl.href.indexOf("#");if(0<=a&&(a=_gjwl.href.substring(a),0<a.indexOf("&q=")||0<=a.indexOf("#q="))&&(a=a.substring(1),-1==a.indexOf("#"))){for(var d=0;d<a.length;){var b=d;"&"==a.charAt(b)&&++b;var c=a.indexOf("&",b);-1==c&&(c=a.length);b=a.substring(b,c);if(0==b.indexOf("fp="))a=a.substring(0,d)+a.substring(c,a.length),c=d;else if("cad=h"==b)return 0;d=c}_gjwl.href="/search?"+a+"&cad=h";return 1}return 0}
 5
+function _gjh(){!_gjuc()&&window.google&&google.x&&google.x({id:"GJH"},function(){google.nav&&google.nav.gjh&&google.nav.gjh()})};window._gjh&&_gjh();</script><style>#gbar,#guser{font-size:13px;padding-top:1px !important;}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{text-decoration:underline !important}a.gb1,a.gb4{color:#00c !important}.gbi .gb4{color:#dd8e27 !important}.gbf .gb4{color:#900 !important}
 6
+</style><style>body,td,a,p,.h{font-family:arial,sans-serif}body{margin:0;overflow-y:scroll}#gog{padding:3px 8px 0}td{line-height:.8em}.gac_m td{line-height:17px}form{margin-bottom:20px}.h{color:#36c}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{font-weight:bold;font-style:normal}.lst{height:25px;width:496px}.gsfi,.lst{font:18px arial,sans-serif}.gsfs{font:17px arial,sans-serif}.ds{display:inline-box;display:inline-block;margin:3px 0 4px;margin-left:4px}input{font-family:inherit}a.gb1,a.gb2,a.gb3,a.gb4{color:#11c !important}body{background:#fff;color:black}a{color:#11c;text-decoration:none}a:hover,a:active{text-decoration:underline}.fl a{color:#36c}a:visited{color:#551a8b}a.gb1,a.gb4{text-decoration:underline}a.gb3:hover{text-decoration:none}#ghead a.gb2:hover{color:#fff !important}.sblc{padding-top:5px}.sblc a{display:block;margin:2px 0;margin-left:13px;font-size:11px}.lsbb{background:#eee;border:solid 1px;border-color:#ccc #999 #999 #ccc;height:30px}.lsbb{display:block}.ftl,#fll a{display:inline-block;margin:0 12px}.lsb{background:url(/images/nav_logo229.png) 0 -261px repeat-x;border:none;color:#000;cursor:pointer;height:30px;margin:0;outline:0;font:15px arial,sans-serif;vertical-align:top}.lsb:active{background:#ccc}.lst:focus{outline:none}</style><script></script><link href="/images/branding/product/ico/googleg_lodp.ico" rel="shortcut icon"></head><body bgcolor="#fff"><script>(function(){var src='/images/nav_logo229.png';var iesg=false;document.body.onload = function(){window.n && window.n();if (document.images){new Image().src=src;}
 7
+if (!iesg){document.f&&document.f.q.focus();document.gbqf&&document.gbqf.q.focus();}
 8
+}
 9
+})();</script><div id="mngb">    <div id=gbar><nobr><b class=gb1>Search</b> <a class=gb1 href="http://www.google.co.in/imghp?hl=en&tab=wi">Images</a> <a class=gb1 href="http://maps.google.co.in/maps?hl=en&tab=wl">Maps</a> <a class=gb1 href="https://play.google.com/?hl=en&tab=w8">Play</a> <a class=gb1 href="http://www.youtube.com/?gl=IN&tab=w1">YouTube</a> <a class=gb1 href="http://news.google.co.in/nwshp?hl=en&tab=wn">News</a> <a class=gb1 href="https://mail.google.com/mail/?tab=wm">Gmail</a> <a class=gb1 href="https://drive.google.com/?tab=wo">Drive</a> <a class=gb1 style="text-decoration:none" href="https://www.google.co.in/intl/en/options/"><u>More</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><span id=gbn class=gbi></span><span id=gbf class=gbf></span><span id=gbe></span><a href="http://www.google.co.in/history/optout?hl=en" class=gb4>Web History</a> | <a  href="/preferences?hl=en" class=gb4>Settings</a> | <a target=_top id=gb_70 href="https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=http://www.google.co.in/%3Fgfe_rd%3Dcr%26ei%3De1q9V86CDuKK8QeCkoiACA" class=gb4>Sign in</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div>    </div><center><br clear="all" id="lgpd"><div id="lga"><div style="padding:28px 0 3px"><div style="height:110px;width:276px;background:url(/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.png) no-repeat" title="Google" align="left" id="hplogo" onload="window.lol&&lol()"><div style="color:#777;font-size:16px;font-weight:bold;position:relative;top:70px;left:218px" nowrap="">India</div></div></div><br></div><form action="/search" name="f"><table cellpadding="0" cellspacing="0"><tr valign="top"><td width="25%">&nbsp;</td><td align="center" nowrap=""><input name="ie" value="ISO-8859-1" type="hidden"><input value="en-IN" name="hl" type="hidden"><input name="source" type="hidden" value="hp"><input name="biw" type="hidden"><input name="bih" type="hidden"><div class="ds" style="height:32px;margin:4px 0"><input style="color:#000;margin:0;padding:5px 8px 0 6px;vertical-align:top" autocomplete="off" class="lst" value="" title="Google Search" maxlength="2048" name="q" size="57"></div><br style="line-height:0"><span class="ds"><span class="lsbb"><input class="lsb" value="Google Search" name="btnG" type="submit"></span></span><span class="ds"><span class="lsbb"><input class="lsb" value="I'm Feeling Lucky" name="btnI" onclick="if(this.form.q.value)this.checked=1; else top.location='/doodles/'" type="submit"></span></span></td><td class="fl sblc" align="left" nowrap="" width="25%"><a href="/advanced_search?hl=en-IN&amp;authuser=0">Advanced search</a><a href="/language_tools?hl=en-IN&amp;authuser=0">Language tools</a></td></tr></table><input id="gbv" name="gbv" type="hidden" value="1"></form><div id="gac_scont"></div><div style="font-size:83%;min-height:3.5em"><br><div id="prm"><style>.szppmdbYutt__middle-slot-promo{font-size:small;margin-bottom:32px}.szppmdbYutt__middle-slot-promo a._uFi{display:inline-block;text-decoration:none}.szppmdbYutt__middle-slot-promo img{border:none;margin-right:5px;vertical-align:middle}.szppmdbYutt__middle-slot-promo ._vFi{color:red}</style><div class="szppmdbYutt__middle-slot-promo r-i_ZIfBTTakK8" jsl="$t t-Gza07Ho9En4;$x 0;" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8QnIcBCAQ"><span class="_vFi"></span>Google Translate with improved offline mode. Get it now on <a class="_XIi" href="https://www.google.com/url?q=https://play.google.com/store/apps/details%3Fid%3Dcom.google.android.apps.translate%26hl%3Den%26referrer%3Dutm_source%253Dgoogle%2526utm_campaign%253Doffline%2526utm_name%253Den&amp;source=hpp&amp;id=5085213&amp;ct=8&amp;usg=AFQjCNE_M--N8XI67IQ-_8ZdpQbuVD6giw&amp;sa=X&amp;ved=0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q8IcBCAU" rel="nofollow">Android</a> or <span class="_vFi"></span><a class="_XIi" href="https://www.google.com/url?q=https://itunes.apple.com/app/google-translate/id414706506%3Fpt%3D9008%26ct%3Den-mobile-offline-hpp%26mt%3D8&amp;source=hpp&amp;id=5085213&amp;ct=8&amp;usg=AFQjCNFWpMjUEQNEjRSgr__KaMD6GUxGIA&amp;sa=X&amp;ved=0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q8IcBCAY" rel="nofollow">iOS</a></div></div><div id="als"><style>#als{font-size:small;margin-bottom:24px}#_eEe{display:inline-block;line-height:28px;}#_eEe a{padding:0 3px;}._lEe{display:inline-block;margin:0 2px;white-space:nowrap}._PEe{display:inline-block;margin:0 2px}</style><div id="_eEe">Google.co.in offered in: <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=hi&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCAg">&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=bn&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCAk">&#2476;&#2494;&#2434;&#2482;&#2494;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=te&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCAo">&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=mr&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCAs">&#2350;&#2352;&#2366;&#2336;&#2368;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=ta&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCAw">&#2980;&#2990;&#3007;&#2996;&#3021;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=gu&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCA0">&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=kn&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCA4">&#3221;&#3240;&#3277;&#3240;&#3233;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=ml&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCA8">&#3374;&#3378;&#3375;&#3390;&#3379;&#3330;</a>  <a href="http://www.google.co.in/setprefs?sig=0_sQ1sGBMXUdloWP-6otJqhSXgXVA%3D&amp;hl=pa&amp;source=homepage" data-ved="0ahUKEwjXsY3v0NnOAhVIsY8KHSMdCF8Q2ZgBCBA">&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;</a> </div></div></div><span id="footer"><div style="font-size:10pt"><div style="margin:19px auto;text-align:center" id="fll"><a href="/intl/en/ads/">Advertising Programs</a><a href="http://www.google.co.in/services/">Business Solutions</a><a href="https://plus.google.com/104205742743787718296" rel="publisher">+Google</a><a href="/intl/en/about.html">About Google</a><a href="http://www.google.co.in/setprefdomain?prefdom=US&amp;sig=__A53jsG-eGKvuTPXQTrCuG_Y4Nog%3D" id="fehl">Google.com</a></div></div><p style="color:#767676;font-size:8pt">&copy; 2016 - <a href="/intl/en/policies/privacy/">Privacy</a> - <a href="/intl/en/policies/terms/">Terms</a></p></span></center><script>(function(){window.google.cdo={height:0,width:0};(function(){var a=window.innerWidth,b=window.innerHeight;if(!a||!b)var c=window.document,d="CSS1Compat"==c.compatMode?c.documentElement:c.body,a=d.clientWidth,b=d.clientHeight;a&&b&&(a!=google.cdo.width||b!=google.cdo.height)&&google.log("","","/client_204?&atyp=i&biw="+a+"&bih="+b+"&ei="+google.kEI);})();})();</script><div id="xjsd"></div><div id="xjsi"><script>(function(){function c(b){window.setTimeout(function(){var a=document.createElement("script");a.src=b;document.getElementById("xjsd").appendChild(a)},0)}google.dljp=function(b,a){google.xjsu=b;c(a)};google.dlj=c;})();(function(){window.google.xjsrm=['mids'];})();if(google.y)google.y.first=[];if(!google.xjs){window._=window._||{};window._._DumpException=function(e){throw e};if(google.timers&&google.timers.load.t){google.timers.load.t.xjsls=new Date().getTime();}google.dljp('/xjs/_/js/k\x3dxjs.hp.en_US.eTq7Yqbxgvk.O/m\x3dsb_he,d/rt\x3dj/d\x3d1/t\x3dzcms/rs\x3dACT90oF-3qtoiuclcJNqDkudJ6b-I_QYWA','/xjs/_/js/k\x3dxjs.hp.en_US.eTq7Yqbxgvk.O/m\x3dsb_he,d/rt\x3dj/d\x3d1/t\x3dzcms/rs\x3dACT90oF-3qtoiuclcJNqDkudJ6b-I_QYWA');google.xjs=1;}google.pmc={"sb_he":{"agen":true,"cgen":true,"client":"heirloom-hp","dh":true,"dhqt":true,"ds":"","fl":true,"host":"google.co.in","isbh":28,"jam":0,"jsonp":true,"msgs":{"cibl":"Clear Search","dym":"Did you mean:","lcky":"I\u0026#39;m Feeling Lucky","lml":"Learn more","oskt":"Input tools","psrc":"This search was removed from your \u003Ca href=\"/history\"\u003EWeb History\u003C/a\u003E","psrl":"Remove","sbit":"Search by image","srch":"Google Search"},"nds":true,"ovr":{},"pq":"","refpd":true,"rfs":[],"scd":10,"sce":5,"stok":"_F4Kr1JRZ6vb5B5CwgPm9baCSEI"},"d":{}};google.y.first.push(function(){if(google.med){google.med('init');google.initHistory();google.med('history');}});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);}
10
+</script></div></body></html>
Back to file index

layer.yaml

 1
--- 
 2
+++ layer.yaml
 3
@@ -0,0 +1,27 @@
 4
+"includes":
 5
+- "layer:ibm-im"
 6
+- "layer:ibm-base"
 7
+- "layer:apt"
 8
+- "layer:leadership"
 9
+- "layer:basic"
10
+- "layer:ibm-was-nd"
11
+- "interface:was-nd"
12
+- "interface:dm-node"
13
+- "interface:was-ihs"
14
+"options":
15
+  "basic":
16
+    "packages":
17
+    - "pwgen"
18
+    - "unzip"
19
+    - "curl"
20
+    "use_venv": !!bool "false"
21
+    "include_system_packages": !!bool "false"
22
+  "ibm-im": {}
23
+  "apt":
24
+    "packages": []
25
+  "leadership": {}
26
+  "ibm-was-nd-dm": {}
27
+  "ibm-base": {}
28
+  "ibm-was-nd": {}
29
+"repo": "bzr+ssh://bazaar.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-im/trunk/"
30
+"is": "ibm-was-nd-dm"
Back to file index

lib/charms/__init__.py

1
--- 
2
+++ lib/charms/__init__.py
3
@@ -0,0 +1,2 @@
4
+from pkgutil import extend_path
5
+__path__ = extend_path(__path__, __name__)
Back to file index

lib/charms/apt.py

  1
--- 
  2
+++ lib/charms/apt.py
  3
@@ -0,0 +1,182 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import itertools
 28
+import subprocess
 29
+
 30
+from charmhelpers import fetch
 31
+from charmhelpers.core import hookenv, unitdata
 32
+from charms import reactive
 33
+
 34
+
 35
+__all__ = ['add_source', 'update', 'queue_install', 'install_queued',
 36
+           'installed', 'purge', 'ensure_package_status']
 37
+
 38
+
 39
+def add_source(source, key=None):
 40
+    '''Add an apt source.
 41
+
 42
+    Sets the apt.needs_update state.
 43
+
 44
+    A source may be either a line that can be added directly to
 45
+    sources.list(5), or in the form ppa:<user>/<ppa-name> for adding
 46
+    Personal Package Archives, or a distribution component to enable.
 47
+
 48
+    The package signing key should be an ASCII armoured GPG key. While
 49
+    GPG key ids are also supported, the retrieval mechanism is insecure.
 50
+    There is no need to specify the package signing key for PPAs or for
 51
+    the main Ubuntu archives.
 52
+    '''
 53
+    # Maybe we should remember which sources have been added already
 54
+    # so we don't waste time re-adding them. Is this time significant?
 55
+    fetch.add_source(source, key)
 56
+    reactive.set_state('apt.needs_update')
 57
+
 58
+
 59
+def queue_install(packages, options=None):
 60
+    """Queue one or more deb packages for install.
 61
+
 62
+    The `apt.installed.{name}` state is set once the package is installed.
 63
+
 64
+    If a package has already been installed it will not be reinstalled.
 65
+
 66
+    If a package has already been queued it will not be requeued, and
 67
+    the install options will not be changed.
 68
+
 69
+    Sets the apt.queued_installs state.
 70
+    """
 71
+    if isinstance(packages, str):
 72
+        packages = [packages]
 73
+    # Filter installed packages.
 74
+    store = unitdata.kv()
 75
+    queued_packages = store.getrange('apt.install_queue.', strip=True)
 76
+    packages = {package: options for package in packages
 77
+                if not (package in queued_packages or
 78
+                        reactive.helpers.is_state('apt.installed.' + package))}
 79
+    if packages:
 80
+        unitdata.kv().update(packages, prefix='apt.install_queue.')
 81
+        reactive.set_state('apt.queued_installs')
 82
+
 83
+
 84
+def installed():
 85
+    '''Return the set of deb packages completed install'''
 86
+    return set(state.split('.', 2)[2] for state in reactive.bus.get_states()
 87
+               if state.startswith('apt.installed.'))
 88
+
 89
+
 90
+def purge(packages):
 91
+    """Purge one or more deb packages from the system"""
 92
+    fetch.apt_purge(packages, fatal=True)
 93
+    store = unitdata.kv()
 94
+    store.unsetrange(packages, prefix='apt.install_queue.')
 95
+    for package in packages:
 96
+        reactive.remove_state('apt.installed.{}'.format(package))
 97
+
 98
+
 99
+def update():
100
+    """Update the apt cache.
101
+
102
+    Removes the apt.needs_update state.
103
+    """
104
+    status_set(None, 'Updating apt cache')
105
+    fetch.apt_update(fatal=True)  # Friends don't let friends set fatal=False
106
+    reactive.remove_state('apt.needs_update')
107
+
108
+
109
+def install_queued():
110
+    '''Installs queued deb packages.
111
+
112
+    Removes the apt.queued_installs state and sets the apt.installed state.
113
+
114
+    On failure, sets the unit's workload state to 'blocked' and returns
115
+    False. Package installs remain queued.
116
+
117
+    On success, sets the apt.installed.{packagename} state for each
118
+    installed package and returns True.
119
+    '''
120
+    store = unitdata.kv()
121
+    queue = sorted((options, package)
122
+                   for package, options in store.getrange('apt.install_queue.',
123
+                                                          strip=True).items())
124
+
125
+    installed = set()
126
+    for options, batch in itertools.groupby(queue, lambda x: x[0]):
127
+        packages = [b[1] for b in batch]
128
+        try:
129
+            status_set(None, 'Installing {}'.format(','.join(packages)))
130
+            fetch.apt_install(packages, options, fatal=True)
131
+            store.unsetrange(packages, prefix='apt.install_queue.')
132
+            installed.update(packages)
133
+        except subprocess.CalledProcessError:
134
+            status_set('blocked',
135
+                       'Unable to install packages {}'
136
+                       .format(','.join(packages)))
137
+            return False  # Without setting reactive state.
138
+
139
+    for package in installed:
140
+        reactive.set_state('apt.installed.{}'.format(package))
141
+
142
+    reactive.remove_state('apt.queued_installs')
143
+    return True
144
+
145
+
146
+def ensure_package_status():
147
+    '''Hold or unhold packages per the package_status configuration option.
148
+
149
+    All packages installed using this module and handlers are affected.
150
+
151
+    An mechanism may be added in the future to override this for a
152
+    subset of installed packages.
153
+    '''
154
+    packages = installed()
155
+    if not packages:
156
+        return
157
+    config = hookenv.config()
158
+    package_status = config['package_status']
159
+    changed = reactive.helpers.data_changed('apt.package_status',
160
+                                            (package_status, sorted(packages)))
161
+    if changed:
162
+        if package_status == 'hold':
163
+            hookenv.log('Holding packages {}'.format(','.join(packages)))
164
+            fetch.apt_hold(packages)
165
+        else:
166
+            hookenv.log('Unholding packages {}'.format(','.join(packages)))
167
+            fetch.apt_unhold(packages)
168
+    reactive.remove_state('apt.needs_hold')
169
+
170
+
171
+def status_set(state, message):
172
+    """Set the unit's workload status.
173
+
174
+    Set state == None to keep the same state and just change the message.
175
+    """
176
+    if state is None:
177
+        state = hookenv.status_get()[0]
178
+        if state == 'unknown':
179
+            state = 'maintenance'  # Guess
180
+    if state in ('error', 'blocked'):
181
+        lvl = hookenv.WARNING
182
+    else:
183
+        lvl = hookenv.INFO
184
+    hookenv.status_set(state, message)
185
+    hookenv.log('{}: {}'.format(state, message), lvl)
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

lib/charms/leadership.py

 1
--- 
 2
+++ lib/charms/leadership.py
 3
@@ -0,0 +1,58 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.reactive import not_unless
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']
28
+
29
+
30
+@not_unless('leadership.is_leader')
31
+def leader_set(settings=None, **kw):
32
+    '''Change leadership settings, per charmhelpers.core.hookenv.leader_set.
33
+
34
+    The leadership.set.{key} reactive state will be set while the
35
+    leadership hook environment setting remains set.
36
+
37
+    Changed leadership settings will set the leadership.changed.{key}
38
+    and leadership.changed states. These states will remain set until
39
+    the following hook.
40
+
41
+    These state changes take effect immediately on the leader, and
42
+    in future hooks run on non-leaders. In this way both leaders and
43
+    non-leaders can share handlers, waiting on these states.
44
+    '''
45
+    settings = settings or {}
46
+    settings.update(kw)
47
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
48
+
49
+    for key, value in settings.items():
50
+        if value != previous.get(key):
51
+            reactive.set_state('leadership.changed.{}'.format(key))
52
+            reactive.set_state('leadership.changed')
53
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
54
+                                      value is not None)
55
+    hookenv.leader_set(settings)
56
+    unitdata.kv().update(settings, prefix='leadership.settings.')
57
+
58
+
59
+def leader_get(attribute=None):
60
+    '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.'''
61
+    return hookenv.leader_get(attribute)
Back to file index

metadata.yaml

 1
--- 
 2
+++ metadata.yaml
 3
@@ -0,0 +1,59 @@
 4
+"name": "ibm-was-nd-dm"
 5
+"summary": "WebSphere Application Server Network Deployment Deployment Manager"
 6
+"maintainer": "IBM Juju Support Team <jujusupp@us.ibm.com>"
 7
+"description": |
 8
+  IBM WebSphere Application Server Network Deployment provides an advanced, flexible runtime environment for large-scale application deployments. It offers near-continuous availability with advanced performance and management capabilities for mission-critical applications.
 9
+"tags":
10
+- "ibm"
11
+- "misc"
12
+- "ibm"
13
+- "im"
14
+- "ibm"
15
+- "apt"
16
+- "leadership"
17
+- "ibm"
18
+- "misc"
19
+"provides":
20
+  "dm-node":
21
+    "interface": "dm-node"
22
+  "was-ihs":
23
+    "interface": "was-ihs"
24
+  "was-nd":
25
+    "interface": "was-nd"
26
+"resources":
27
+  "ibm_was_nd_installer1":
28
+    "type": "file"
29
+    "filename": "ibm_was_nd_installer1.zip"
30
+    "description": "WAS ND installer part1 archive"
31
+  "ibm_was_nd_installer2":
32
+    "type": "file"
33
+    "filename": "ibm_was_nd_installer2.zip"
34
+    "description": "WAS ND installer part2 archive"
35
+  "ibm_was_nd_installer3":
36
+    "type": "file"
37
+    "filename": "ibm_was_nd_installer3.zip"
38
+    "description": "WAS ND installer part3 archive"
39
+  "ibm_was_nd_fp1":
40
+    "type": "file"
41
+    "filename": "ibm_was_nd_fp1.zip"
42
+    "description": "WAS ND fix pack part1 archive"
43
+  "ibm_was_nd_fp2":
44
+    "type": "file"
45
+    "filename": "ibm_was_nd_fp2.zip"
46
+    "description": "WAS ND fix pack part2 archive"
47
+  "ibm_im_installer":
48
+    "type": "file"
49
+    "filename": "ibm_im_installer.zip"
50
+    "description": "Installation Manager installer archive"
51
+  "ibm_im_fixpack":
52
+    "type": "file"
53
+    "filename": "ibm_im_fixpack.zip"
54
+    "description": "Installation Manager fix pack archive"
55
+"series":
56
+- "trusty"
57
+- "xenial"
58
+"subordinate": !!bool "false"
59
+"terms":
60
+- "ibm-was-nd/1"
61
+- "ibm-im/1"
62
+- "ibm-was-nd/1"
Back to file index

reactive/apt.py

  1
--- 
  2
+++ reactive/apt.py
  3
@@ -0,0 +1,131 @@
  4
+# Copyright 2015-2016 Canonical Ltd.
  5
+#
  6
+# This file is part of the Apt layer for Juju.
  7
+#
  8
+# This program is free software: you can redistribute it and/or modify
  9
+# it under the terms of the GNU General Public License version 3, as
 10
+# published by the Free Software Foundation.
 11
+#
 12
+# This program is distributed in the hope that it will be useful, but
 13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
 14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 15
+# PURPOSE.  See the GNU General Public License for more details.
 16
+#
 17
+# You should have received a copy of the GNU General Public License
 18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 19
+
 20
+'''
 21
+charms.reactive helpers for dealing with deb packages.
 22
+
 23
+Add apt package sources using add_source(). Queue deb packages for
 24
+installation with install(). Configure and work with your software
 25
+once the apt.installed.{packagename} state is set.
 26
+'''
 27
+import subprocess
 28
+
 29
+from charmhelpers import fetch
 30
+from charmhelpers.core import hookenv
 31
+from charmhelpers.core.hookenv import WARNING
 32
+from charms import layer
 33
+from charms import reactive
 34
+from charms.reactive import when, when_not
 35
+
 36
+import charms.apt
 37
+
 38
+
 39
+@when('apt.needs_update')
 40
+def update():
 41
+    charms.apt.update()
 42
+
 43
+
 44
+@when('apt.queued_installs')
 45
+@when_not('apt.needs_update')
 46
+def install_queued():
 47
+    charms.apt.install_queued()
 48
+
 49
+
 50
+@when_not('apt.queued_installs')
 51
+def ensure_package_status():
 52
+    charms.apt.ensure_package_status()
 53
+
 54
+
 55
+def filter_installed_packages(packages):
 56
+    # Don't use fetch.filter_installed_packages, as it depends on python-apt
 57
+    # and not available if the basic layer's use_site_packages option is off
 58
+    # TODO: Move this to charm-helpers.fetch
 59
+    cmd = ['dpkg-query', '--show', r'--showformat=${Package}\n']
 60
+    installed = set(subprocess.check_output(cmd,
 61
+                                            universal_newlines=True).split())
 62
+    return set(packages) - installed
 63
+
 64
+
 65
+def clear_removed_package_states():
 66
+    """On hook startup, clear install states for removed packages."""
 67
+    removed = filter_installed_packages(charms.apt.installed())
 68
+    if removed:
 69
+        hookenv.log('{} missing packages ({})'.format(len(removed),
 70
+                                                      ','.join(removed)),
 71
+                    WARNING)
 72
+        for package in removed:
 73
+            reactive.remove_state('apt.installed.{}'.format(package))
 74
+
 75
+
 76
+def configure_sources():
 77
+    """Add user specified package sources from the service configuration.
 78
+
 79
+    See charmhelpers.fetch.configure_sources for details.
 80
+    """
 81
+    config = hookenv.config()
 82
+
 83
+    # We don't have enums, so we need to validate this ourselves.
 84
+    package_status = config.get('package_status')
 85
+    if package_status not in ('hold', 'install'):
 86
+        charms.apt.status_set('blocked',
 87
+                              'Unknown package_status {}'
 88
+                              ''.format(package_status))
 89
+        # Die before further hooks are run. This isn't very nice, but
 90
+        # there is no other way to inform the operator that they have
 91
+        # invalid configuration.
 92
+        raise SystemExit(0)
 93
+
 94
+    sources = config.get('install_sources')
 95
+    keys = config.get('install_keys')
 96
+    if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)):
 97
+        fetch.configure_sources(update=False,
 98
+                                sources_var='install_sources',
 99
+                                keys_var='install_keys')
100
+        reactive.set_state('apt.needs_update')
101
+
102
+    extra_packages = sorted(config.get('extra_packages', '').split())
103
+    if extra_packages:
104
+        charms.apt.queue_install(extra_packages)
105
+
106
+
107
+def queue_layer_packages():
108
+    """Add packages listed in build-time layer options."""
109
+    # Both basic and apt layer. basic layer will have already installed
110
+    # its defined packages, but rescheduling it here gets the apt layer
111
+    # state set and they will pinned as any other apt layer installed
112
+    # package.
113
+    opts = layer.options()
114
+    for section in ['basic', 'apt']:
115
+        if section in opts and 'packages' in opts[section]:
116
+            charms.apt.queue_install(opts[section]['packages'])
117
+
118
+
119
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
120
+# this module may be imported multiple times so ensure the
121
+# initialization hook is only registered once. I have to piggy back
122
+# onto the namespace of a module imported before reactive discovery
123
+# to do this.
124
+if not hasattr(reactive, '_apt_registered'):
125
+    # We need to register this to run every hook, not just during install
126
+    # and config-changed, to protect against race conditions. If we don't
127
+    # do this, then the config in the hook environment may show updates
128
+    # to running hooks well before the config-changed hook has been invoked
129
+    # and the intialization provided an opertunity to be run.
130
+    hookenv.atstart(hookenv.log, 'Initializing Apt Layer')
131
+    hookenv.atstart(clear_removed_package_states)
132
+    hookenv.atstart(configure_sources)
133
+    hookenv.atstart(queue_layer_packages)
134
+    reactive._apt_registered = True
Back to file index

reactive/ibm-base.sh

  1
--- 
  2
+++ reactive/ibm-base.sh
  3
@@ -0,0 +1,107 @@
  4
+#!/bin/bash
  5
+source charms.reactive.sh
  6
+set -e
  7
+
  8
+
  9
+# Utility function to verify a downloaded resource
 10
+# :param: file name
 11
+# :param: checksum type
 12
+# :param: checksum value
 13
+function verify_curl_resource() {
 14
+  local FILE=$1
 15
+  local TYPE=$2
 16
+  local EXPECTED_SUM=$3
 17
+  local CALCULATED_SUM=""
 18
+  local PROG=""
 19
+
 20
+  if [ ! -r ${FILE} ]; then
 21
+    status-set blocked "ibm-base: could not read ${FILE}"
 22
+    juju-log "Could not verify the downloaded resource. File could not be read: ${FILE}"
 23
+  fi
 24
+
 25
+  # Set our checksum utility based on the requested type
 26
+  case "${TYPE}" in
 27
+    md5)
 28
+      PROG='md5sum'
 29
+      ;;
 30
+    sha256)
 31
+      PROG='sha256sum'
 32
+      ;;
 33
+    sha512)
 34
+      PROG='sha512sum'
 35
+      ;;
 36
+    *)
 37
+      status-set blocked "ibm-base: checksum type must be md5, sha215, or sha512"
 38
+      juju-log "Could not verify the downloaded resource ${FILE}. Unknown checksum type: ${TYPE}"
 39
+      return 1
 40
+  esac
 41
+
 42
+  CALCULATED_SUM=`${PROG} ${FILE} | awk '{print $1}'`
 43
+  if [ "${CALCULATED_SUM}" = "${EXPECTED_SUM}" ]; then
 44
+    juju-log "Checksum verified for ${FILE}."
 45
+    return 0
 46
+  else
 47
+    status-set blocked "ibm-base: checksums did not match"
 48
+    juju-log "Checksum mismatch for ${FILE}. Expected ${EXPECTED_SUM}, got ${CALCULATED_SUM}"
 49
+    return 1
 50
+  fi
 51
+}
 52
+
 53
+
 54
+# Fetch curl resources if a URL is configured
 55
+@when 'config.set.curl_url'
 56
+@when_any 'config.new.curl_url' 'config.changed.curl_url' 'config.new.curl_opts' 'config.changed.curl_opts'
 57
+function fetch_curl_resource() {
 58
+  local ARCHIVE_DIR="${CHARM_DIR}/files/archives"
 59
+  local CURL_URL=$(config-get 'curl_url')
 60
+  local CURL_OPTS=$(config-get 'curl_opts')
 61
+
 62
+  status-set maintenance "ibm-base: fetching resource(s)"
 63
+
 64
+  mkdir -p ${ARCHIVE_DIR}
 65
+  cd ${ARCHIVE_DIR}
 66
+  # Multiple URLs may be separated by a space, so loop.
 67
+  for URL_STRING in ${CURL_URL}
 68
+  do
 69
+    # For each URL_STRING, set the url, checksum type, and checksum value.
 70
+    local URL=${URL_STRING%%\?*}        # string before the first '?'
 71
+    local FILE_NAME=${URL##*\/}         # string after the last '/'
 72
+    local SUM_STRING=${URL_STRING#*\?}  # string after the first '?'
 73
+    local SUM_TYPE=${SUM_STRING%%\=*}   # string before the first '='
 74
+    local SUM_VALUE=${SUM_STRING#*\=}   # string after the first '='
 75
+
 76
+    if [ -z ${FILE_NAME} ]; then
 77
+      FILE_NAME="juju-${RANDOM}"
 78
+    fi
 79
+    curl --silent --show-error ${CURL_OPTS} -o ${FILE_NAME} ${URL}
 80
+
 81
+    # Verify our resource checksum. If this fails, let verify_resource log
 82
+    # the reason and exit successfully. Exiting non-zero would fail the hook,
 83
+    # so return 0 and simply inform the user that verification failed.
 84
+    verify_curl_resource ${FILE_NAME} ${SUM_TYPE} ${SUM_VALUE} || return 0
 85
+  done
 86
+  cd -
 87
+
 88
+  set_state 'ibm-base.curl.resource.fetched'
 89
+  status-set active "ibm-base: curl resource(s) fetched"
 90
+  juju-log 'Curl resource fetched'
 91
+}
 92
+
 93
+
 94
+# Handle license acceptance
 95
+@when 'config.changed.license_accepted'
 96
+function check_license_acceptance() {
 97
+  local LIC_ACCEPTED=$(config-get 'license_accepted')
 98
+
 99
+  # compare lowercase LIC_ACCEPTED (requires bash > 4)
100
+  if [ "${LIC_ACCEPTED,,}" = "true" ]; then
101
+    set_state 'ibm-base.license.accepted'
102
+    juju-log 'License accepted'
103
+  else
104
+    remove_state 'ibm-base.license.accepted'
105
+    juju-log 'License NOT accepted'
106
+  fi
107
+}
108
+
109
+# Main reactive entry point
110
+reactive_handler_main
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" ]; then
 17
+            juju-log "IBM IM: only supported on x86_64 or ppc64le platforms"
 18
+            status-set blocked "unsupported architecture"
 19
+            exit 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
+        exit 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
+                      exit 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
+            exit 1
 69
+        fi
 70
+      else
 71
+        juju-log "IBM IM: installer was not found."
 72
+        status-set blocked "installation failed"
 73
+        exit 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
+      exit 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
+      exit 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
+                      exit 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
+             exit 1
139
+        fi
140
+    else
141
+        juju-log "IBM_IM: Fix pack installer was not found."
142
+        status-set blocked "fixpack installation failed"
143
+        exit 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
+        exit 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-nd-dm.sh

  1
--- 
  2
+++ reactive/ibm-was-nd-dm.sh
  3
@@ -0,0 +1,456 @@
  4
+#!/bin/bash
  5
+
  6
+set -ex
  7
+
  8
+source charms.reactive.sh
  9
+
 10
+reactive_states=`charms.reactive -y get_states`
 11
+
 12
+WAS_ND_INSTALL_PATH=/root/IBM/WebSphere/AppServer/V85/ND
 13
+IM_INSTALL_PATH=/opt/IBM/InstallationManager
 14
+ARCHITECTURE=`uname -m`
 15
+profile_name="dmgr"
 16
+old_dm_admin=""
 17
+old_dm_pw=""
 18
+dm_admin_name=`config-get dm_admin_user`
 19
+dm_admin_pw=`config-get dm_admin_pw`
 20
+profilepath=/root/IBM/WebSphere/AppServer/V85/ND/profiles
 21
+cluster_prefix="dyncluster"
 22
+profile_template_path="$WAS_ND_INSTALL_PATH/profileTemplates/dmgr"
 23
+ihsadminuser="usr"
 24
+ihsadminpw=`pwgen -N 1 15`
 25
+ihsadmingrp="grp"
 26
+
 27
+if [ -f $CHARM_DIR/files/was_users.txt ]; then
 28
+	source $CHARM_DIR/files/was_users.txt
 29
+	old_dm_admin=$user_name
 30
+	old_dm_pw=$password
 31
+fi
 32
+
 33
+#Get the cfguser name based on the remote unit name
 34
+get_cfgusername()
 35
+{
 36
+	cfgusername=$1
 37
+	cfgusername=`echo $cfgusername | cut -d"/" -f1`
 38
+	cfgusername=`echo "$cfgusername" | sed -r 's/-//g' | sed -r 's/ibm//g'`
 39
+	cfgusername=`echo "$cfgusername" | awk '{print substr($0,0,5)}'`
 40
+	ihsadminuser="$cfgusername$ihsadminuser"
 41
+	ihsadmingrp="$cfgusername$ihsadmingrp"
 42
+
 43
+}
 44
+
 45
+get_clustername(){
 46
+	cellname=`ls $profilepath/$profile_name/config/cells`
 47
+	if [ -d $profilepath/$profile_name/config/cells/$cellname/clusters ]
 48
+	then
 49
+		clustername=`ls $profilepath/$profile_name/config/cells/$cellname/clusters`
 50
+		clustername=`basename $clustername`
 51
+	else
 52
+		juju-log "IBM WAS ND DM: Cluster does not exist."
 53
+	fi
 54
+	echo "$clustername"
 55
+}
 56
+
 57
+stop_was_nd_dmgr() {
 58
+	if [ "$1" != "" -a "$1" != $dm_admin_name ]; then
 59
+		dm_admin_name=$1
 60
+	fi
 61
+	if [ "$2" != "" -a "$2" != $dm_admin_pw ]; then
 62
+		dm_admin_pw=$2
 63
+	fi
 64
+	
 65
+	juju-log "profile_name===="$profile_name"==dm_admin_name==="$dm_admin_name"====dm_admin_pw=="$dm_admin_pw"profilepath====$profilepath"
 66
+	if [ -d "$profilepath/$profile_name/bin" ]; then
 67
+		cd $profilepath/$profile_name/bin/
 68
+		if ! ./stopManager.sh -username $dm_admin_name -password $dm_admin_pw
 69
+		then
 70
+			status-set blocked "Error while stoping the deployment manager"
 71
+		else
 72
+			status-set active "Deployment Manager stopped successfully"
 73
+			close-port 9060
 74
+			close-port 9043
 75
+			remove_state 'ibm-was-nd-dm.manager.started'
 76
+		fi
 77
+	else
 78
+		juju-log "IBM WAS ND DM: No deployment manager to stop, exiting."
 79
+		return 0
 80
+	fi
 81
+}
 82
+
 83
+
 84
+install_was_nd_fixpack() {
 85
+	if [ "$ARCHITECTURE" == "x86_64" ];then
 86
+		if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.ND.v85 -acceptLicense -repositories $CHARM_DIR/../resources/WAS_ND/FP -installationDirectory $WAS_ND_INSTALL_PATH  -showProgress
 87
+		then
 88
+			set_state 'ibm-was-nd-dm.updated'
 89
+			status-set active "IBM WAS ND DM: WAS ND installation upgraded successfully!"
 90
+		else
 91
+			status-set blocked "IBM WAS ND DM: Error while upgrading WAS ND!"
 92
+			return 1
 93
+		fi
 94
+                          
 95
+	elif [ "$ARCHITECTURE" == "ppc64le" ];then
 96
+		if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.ND.le.v85 -acceptLicense -repositories $CHARM_DIR/../resources/WAS_ND/FP -installationDirectory $WAS_ND_INSTALL_PATH  -showProgress
 97
+		then
 98
+			set_state 'ibm-was-nd-dm.updated'
 99
+			status-set active "IBM WAS ND DM: WAS ND installation upgraded successfully!"
100
+		else
101
+			status-set blocked "IBM WAS ND DM: Error while upgrading WAS ND!"
102
+			return 1
103
+		fi
104
+	fi
105
+}
106
+
107
+@when 'ibm-was-nd.installed'
108
+@when_not 'ibm-was-nd-dm.profile.available'
109
+function create_profile(){
110
+	$WAS_ND_INSTALL_PATH/bin/manageprofiles.sh -create -profileName $profile_name -profilePath $profilepath/$profile_name -templatePath $profile_template_path -enableAdminSecurity true -adminUserName $dm_admin_name -adminPassword $dm_admin_pw
111
+	set_state 'ibm-was-nd-dm.profile.available'
112
+	if [ ! -d $CHARM_DIR/files ]; then
113
+		mkdir $CHARM_DIR/files
114
+	fi
115
+	if [ -f $CHARM_DIR/files/was_users.txt ]; then
116
+		juju-log "IBM WAS Base: was_users.txt file already exists."
117
+	else
118
+		touch $CHARM_DIR/files/was_users.txt
119
+	fi
120
+
121
+cat > $CHARM_DIR/files/was_users.txt <<EOF
122
+user_name=$dm_admin_name
123
+password=$dm_admin_pw
124
+EOF
125
+	chmod 600 $CHARM_DIR/files/was_users.txt
126
+}
127
+
128
+@when 'ibm-was-nd-dm.profile.available'
129
+@when_not 'ibm-was-nd-dm.manager.started'
130
+function start_was_nd_dmgr() {
131
+	if [ -d $profilepath/$profile_name/bin ]; then
132
+		cd $profilepath/$profile_name/bin/
133
+		if ! ./startManager.sh
134
+		then
135
+			status-set blocked "IBM WAS ND DM: Error while starting the deployment manager"
136
+		else
137
+			status-set active "IBM WAS ND DM: Deployment Manager started successfully"
138
+			open-port 9060
139
+			open-port 9043
140
+			set_state 'ibm-was-nd-dm.manager.started'
141
+		fi
142
+	else
143
+		status-set blocked "IBM WAS ND DM: No deployment manager to start, exiting."
144
+		return 0
145
+	fi
146
+}
147
+
148
+@when_not_all 'config.default.was_admin_user' 'config.default.dm_admin_pw'
149
+@when_any 'config.changed.was_admin_user' 'config.changed.dm_admin_pw'
150
+function create_new_profile() {
151
+	if  charms.reactive is_state 'was-ihs.available'; then
152
+		status-set blocked "IBM WAS ND DM: Please remove the relation with IHS before creating new profile."
153
+		return 0
154
+	fi
155
+	if  charms.reactive is_state 'dm-node.connected'; then
156
+		status-set blocked "IBM WAS ND DM: Please remove the relation with all WAS ND nodes before creating new profile."
157
+		return 0
158
+	fi
159
+	
160
+	if  charms.reactive is_state 'was-nd.connected'; then
161
+		status-set blocked "IBM WAS ND DM: Please remove the relation with all consumer charms before creating new profile."
162
+		return 0
163
+	fi
164
+	
165
+	if [ "$dm_admin_name" == "" ]; then
166
+		status-set blocked "IBM WAS ND DM: Please set the was admin name through dm_admin_name config option."
167
+		return 0
168
+	fi
169
+
170
+	if [ "$dm_admin_pw" == "" ]; then
171
+		status-set blocked "IBM WAS ND DM: Please set the was admin password through dm_admin_pw config option."
172
+		return 0
173
+	fi
174
+	
175
+	juju-log "old_dm_admin_user=$old_dm_admin, old_dm_admin_password=$old_dm_pw"
176
+	if [ -d $profilepath ]; then
177
+		stop_was_nd_dmgr $old_dm_admin $old_dm_pw 
178
+		cd /
179
+		$WAS_ND_INSTALL_PATH/bin/manageprofiles.sh -delete -profileName $profile_name
180
+		rm -rf $profilepath
181
+		juju-log "IBM WAS ND: Removed old dmgr profile to create new dmgr profile with new user id and password"
182
+		remove_state 'ibm-was-nd-dm.profile.available'
183
+	fi
184
+					
185
+}
186
+
187
+
188
+@when 'ibm-was-nd.installed'
189
+@when_not 'ibm-was-nd-dm.updated'
190
+function update_was_nd(){
191
+	
192
+	# Get the WAS ND fixpack resources
193
+	juju-log "IBM WAS ND DM: fetching the ibm_was_nd_fp1 resource"
194
+	status-set maintenance "fetching the ibm_was_nd_fp1 resource"
195
+	was_nd_fp1=`resource-get 'ibm_was_nd_fp1' || echo unavailable`
196
+		
197
+	juju-log "IBM WAS ND DM: fetching the ibm_was_nd_fp2 resource"
198
+	status-set maintenance "fetching the ibm_was_nd_fp2 resource"
199
+	was_nd_fp2=`resource-get 'ibm_was_nd_fp2' || echo unavailable`
200
+		
201
+	#If we don't have a fixpack, just exit successfully; there's nothing to do.
202
+	if [ "$was_nd_fp1" = "unavailable" -o "$was_nd_fp2" = "unavailable" ]; then
203
+		juju-log "IBM WAS ND DM: no ibm_was_nd_fixpack packages to install"
204
+		status-set active "IBM WAS ND DM is ready"
205
+		return 0
206
+	fi
207
+	
208
+	#If we detect the fixpack is just a placeholder in charm store, just exit successfully; there's nothing to do.
209
+	ibm_was_fp1_isempty=`file $was_nd_fp1 | { grep -q empty && echo "True"; } || echo "False"`
210
+	ibm_was_fp2_isempty=`file $was_nd_fp1 | { grep -q empty && echo "True"; } || echo "False"`
211
+	if [ "$ibm_was_fp1_isempty" = "True" -o "$ibm_was_fp2_isempty" = "True" ]; then
212
+		juju-log "IBM WAS ND DM: no ibm_was_nd_fixpack packages to install"
213
+		status-set active "IBM WAS ND DM is ready"
214
+		return 0
215
+	fi
216
+	was_fixpack_packages="$was_nd_fp1 $was_nd_fp2"
217
+	juju-log "IBM WAS ND DM: Fixpacks provided for upgrade $was_fixpack_packages"
218
+	
219
+	if [ ! -d $CHARM_DIR/../resources/WAS_ND/FP ]; then
220
+		mkdir $CHARM_DIR/../resources/WAS_ND/FP
221
+	fi
222
+	
223
+	for was_fp_pkg in $was_fixpack_packages; do
224
+		juju-log "IBM WAS ND DM: was_fp_pkg="$was_fp_pkg
225
+		if [ -f $was_fp_pkg ]; then
226
+			ARCHIVE_DIR=`dirname $was_fp_pkg`
227
+			juju-log "IBM WAS ND DM: extracting the ibm_was_nd_fp resources"
228
+			status-set maintenance "extracting the ibm_was_nd_fp resources"
229
+			if ! unzip $was_fp_pkg -d $ARCHIVE_DIR/../WAS_ND/FP
230
+				then
231
+					juju-log "IBM WAS ND DM: Unable to extract the WAS ND fixpack content. Verify whether the package is corrupt."
232
+					# Remove corrupt archive file
233
+					status-set blocked "IBM WAS ND packages are corrupt"
234
+					rm -rf $ARCHIVE_DIR/../WAS_ND/FP
235
+					exit 0
236
+			else
237
+				juju-log "IBM WAS ND DM: $was_fp_pkg extracted successfully"
238
+			fi
239
+		fi
240
+	done
241
+	if charms.reactive is_state 'ibm-was-nd-dm.manager.started'; then
242
+		stop_was_nd_dmgr $old_dm_admin $old_dm_pw
243
+	fi
244
+	install_was_nd_fixpack
245
+}
246
+
247
+@hook 'upgrade-charm'
248
+function check_fixpack(){
249
+	# The upgrade-charm hook will fire when a new resource is pushed for this
250
+	# charm. This is a good time to determine if we need to deal with a new
251
+	# fixpack.
252
+	if ! charms.reactive is_state 'ibm-was-nd.updated'; then
253
+		# If there is no prior fixpack installed (because ibm-was-nd.updated is not
254
+		# set), do nothing since install_was_nd_fixpack will handle that case.
255
+		juju-log "IBM WAS ND DM: no fixpack has been installed; nothing to upgrade."
256
+		return 0
257
+	else
258
+		# If we have a fixpack packges already (because ibm-was-nd.updated is set),
259
+		# we should fetch the latest fixpack packages and determine if it is new.
260
+		#  - If it is new, set our states so install_was_nd_fixpack is called again
261
+		#  - If it is not new, do nothing
262
+		juju-log "IBM WAS ND DM: scanning for new fixpacks to install"
263
+		WAS_FP1_DIR="$CHARM_DIR/../resources/ibm_was_nd_fp1"
264
+		WAS_FP2_DIR="$CHARM_DIR/../resources/ibm_was_nd_fp2"
265
+		CUR_FP1="$WAS_FP1_DIR/ibm_was_nd_fp1.zip"
266
+		CUR_FP2="$WAS_FP2_DIR/ibm_was_nd_fp2.zip"
267
+		if [ -f $CUR_FP1 -a -f  $CUR_FP2 ]; then
268
+			CUR_FP1_MD5=`md5sum "$CUR_FP1" | awk '{print $1}'`
269
+			CUR_FP2_MD5=`md5sum "$CUR_FP2" | awk '{print $1}'`
270
+		fi
271
+		NEW_FP1=`resource-get 'ibm_was_nd_fp1' || echo unavailable`
272
+		NEW_FP2=`resource-get 'ibm_was_nd_fp2' || echo unavailable`
273
+		
274
+		if [ "$NEW_FP1" = "unavailable" -o "$NEW_FP2" = "unavailable" ]; then
275
+			juju-log "IBM WAS ND DM: no fixpacks to install"
276
+		elif [ -f $NEW_FP1 -a -f  $NEW_FP2 ]; then
277
+			NEW_FP1_MD5=`md5sum "$NEW_FP1" | awk '{print $1}'`
278
+			NEW_FP2_MD5=`md5sum "$NEW_FP2" | awk '{print $1}'`
279
+		fi
280
+		
281
+		if [ "$CUR_FP1_MD5" != "$NEW_FP1_MD5" -o "$CUR_FP2_MD5" != "$NEW_FP2_MD5" ]; then
282
+			juju-log "IBM WAS ND DM: new fixpack detected ($CUR_FP1 with $CUR_FP1_MD5 versus $NEW_FP1 with $NEW_FP1_MD5 and $CUR_FP2 with $CUR_FP2_MD5 versus $NEW_FP2 with $NEW_FP2_MD5)"
283
+			rm -rf $CHARM_DIR/../resources/WAS_ND/FP
284
+			remove_state 'ibm-was-nd.updated'
285
+		else
286
+			juju-log "IBM WAS ND DM: no new fixpack to install"
287
+		fi
288
+	fi
289
+}
290
+
291
+@when 'ibm-was-nd-dm.manager.started' 'dm-node.connected'
292
+@when_not 'ibm-was-nd-dm.exposed'
293
+function expose_was_nd_dm_details(){
294
+		juju-log "IBM WAS ND DM: ************************In expose_was_nd_dm_details function ***********"
295
+		host_name=`unit-get private-address`
296
+		host_name=`hostname`
297
+		services=$(relation_call --state=dm-node.connected services)
298
+		for service in $services; do
299
+			juju-log "IBM WAS ND DM: hostname = $host_name  dminstallpath== $WAS_ND_INSTALL_PATH  profilename= $profile_name username = $dm_admin_name  password=$dm_admin_pw"
300
+			juju-log "IBM WAS ND DM: Setting was nd dm details for $service"
301
+			relation_call --state=dm-node.connected set_was_dm_details $host_name $WAS_ND_INSTALL_PATH $profile_name $dm_admin_name $dm_admin_pw || true
302
+		done
303
+		set_state 'ibm-was-nd-dm.exposed'
304
+}
305
+
306
+@when 'ibm-was-nd-dm.manager.started' 'was-nd.connected'
307
+@when_not 'ibm-was-nd-dm.consumer.exposed'
308
+function expose_was_nd_dm_details_to_consumer(){
309
+		juju-log "IBM WAS ND DM: ************************In expose_was_nd_dm_details_to_consumer function ***********"
310
+		host_name=`unit-get private-address`
311
+		services=$(relation_call --state=was-nd.connected services)
312
+		for service in $services; do
313
+			juju-log "IBM WAS ND DM: hostname = $host_name  dminstallpath== $WAS_ND_INSTALL_PATH  profilename= $profile_name username = $dm_admin_name  password=$dm_admin_pw"
314
+			juju-log "IBM WAS ND DM: Setting was nd dm details for $service"
315
+			relation_call --state=was-nd.connected set_was_dm_details $host_name $WAS_ND_INSTALL_PATH $profile_name $dm_admin_name $dm_admin_pw || true
316
+		done
317
+		set_state 'ibm-was-nd-dm.consumer.exposed'
318
+}
319
+
320
+@when 'was-nd.departed'
321
+function remove_relation_consumer(){
322
+	juju-log "IBM WAS ND DM: ************************In remove_relation_consumer function ***********"
323
+	remove_state 'ibm-was-nd-dm.consumer.exposed'
324
+	juju-log "Relation removed from consumer charm."
325
+	
326
+}
327
+@when 'ibm-was-nd-dm.manager.started' 'dm-node.connected' 'dm-node.available'
328
+function update_etc_hosts_file(){
329
+	juju-log "IBM WAS ND : Adding the WAS ND Node IP's to etc/hosts."
330
+	ip=$(relation_call --state=dm-node.available get_was_node_ip) || true
331
+	juju-log "IBM WAS ND DM : IP and hostname of the WAS ND Node will be added to the Hosts file of DM...: $ip"
332
+	for i in $ip
333
+	do
334
+		juju-log "IBM WAS ND DM : IP and hostname of WAS ND Node connecting is : $i"
335
+		cd /etc
336
+		if grep -q "$i" hosts
337
+		then
338
+			juju-log "IBM WAS ND: Host file already updated with $i"
339
+		else
340
+			juju-log "IBM WAS ND: Updating Host file with $i"
341
+			echo "$i" >> /etc/hosts
342
+			if [ -f $CHARM_DIR/files/start_cluster.py ]; then
343
+				$WAS_ND_INSTALL_PATH/bin/wsadmin.sh -lang jython -f $CHARM_DIR/files/start_cluster.py -username $dm_admin_name -password $dm_admin_pw
344
+			fi
345
+		fi
346
+	done
347
+	
348
+}
349
+
350
+@when 'ibm-was-nd-dm.exposed' 'dm-node.connected'
351
+@when_not 'ibm-was-nd-dm.cluster.configured'
352
+function configure_dynamic_cluster(){
353
+	juju-log "IBM WAS ND DM: ************************In configure_dynamic_cluster function ***********"
354
+	juju-log "IBM WAS ND DM: Creating Dynamic cluster"
355
+	juju-log "IBM WAS ND DM: cluster prefix is: $cluster_prefix"
356
+	$WAS_ND_INSTALL_PATH/bin/wsadmin.sh -lang jython -f $WAS_ND_INSTALL_PATH/bin/CreateDynamicCluster.py -dcPrefix:$cluster_prefix -ngName:DefaultNodeGroup -opMode:manual -username $dm_admin_name -password $dm_admin_pw
357
+	clustername=$( get_clustername )
358
+	juju-log "cluster_name=====$clustername"
359
+	juju-log "IBM WAS ND DM: Jython script to start dynamic cluster."
360
+	if [ -f $CHARM_DIR/files/start_cluster.py ]; then
361
+		$WAS_ND_INSTALL_PATH/bin/wsadmin.sh -lang jython -f $CHARM_DIR/files/start_cluster.py -username $dm_admin_name -password $dm_admin_pw
362
+	else
363
+		touch $CHARM_DIR/files/start_cluster.py
364
+		chmod +x $CHARM_DIR/files/start_cluster.py
365
+			cat > $CHARM_DIR/files/start_cluster.py <<EOF
366
+cell = AdminControl.getCell()
367
+print " Cell name is --> "+ cell
368
+Cluster = AdminControl.completeObjectName('cell='+ cell +',type=Cluster,name=$clustername,*')
369
+state = AdminControl.getAttribute(Cluster, 'state')
370
+if (state == 'websphere.cluster.running'):
371
+	print "Cluster --> $clustername is running .......... "
372
+	print "Ripple starting cluster ............."
373
+	clusterMgr = AdminControl.completeObjectName('cell='+ cell +',type=ClusterMgr,*')
374
+	print AdminControl.invoke(clusterMgr, 'retrieveClusters')
375
+	Cluster = AdminControl.completeObjectName('cell='+ cell +',type=Cluster,name=$clustername,*')
376
+	print AdminControl.invoke(Cluster ,'rippleStart')
377
+else:
378
+	print "Cluster --> $clustername is stopped "
379
+	print "Starting cluster ............... "
380
+	clusterMgr = AdminControl.completeObjectName('cell='+ cell +',type=ClusterMgr,*')
381
+	AdminControl.invoke(clusterMgr, 'retrieveClusters')
382
+	Cluster = AdminControl.completeObjectName('cell='+ cell +',type=Cluster,name=$clustername,*')
383
+	print AdminControl.invoke(Cluster ,'start')
384
+	print " ---------------------------------------------------------------------------------------------- "
385
+EOF
386
+	$WAS_ND_INSTALL_PATH/bin/wsadmin.sh -lang jython -f $CHARM_DIR/files/start_cluster.py -username $dm_admin_name -password $dm_admin_pw
387
+	fi
388
+	juju-log "IBM WAS ND DM:Dynamic cluster created"
389
+	set_state 'ibm-was-nd-dm.cluster.configured'
390
+}
391
+
392
+@when 'dm-node.departed'
393
+function remove_was_node_relation(){
394
+	juju-log "IBM WAS ND DM:************************In remove_was_node_relation function ***********"
395
+	services=$(relation_call --state=dm-node.departed services)
396
+	for service in $services; do
397
+		relation_call --state=dm-node.departed dismiss || true
398
+	done
399
+	rm -rf $CHARM_DIR/files/start_cluster.py
400
+	clustername=$( get_clustername )
401
+	juju-log "IBM WAS ND DM: cluster_name=====$clustername"
402
+	if $WAS_ND_INSTALL_PATH/bin/wsadmin.sh -f $WAS_ND_INSTALL_PATH/bin/deleteDynamicCluster.jacl DefaultNodeGroup $clustername -username $dm_admin_name -password $dm_admin_pw
403
+	then
404
+		remove_state 'ibm-was-nd-dm.exposed'
405
+		remove_state 'ibm-was-nd-dm.cluster.configured'
406
+	else
407
+		juju-log "IBM WAS ND DM: Error occured while deleting cluster."
408
+		status-set blocked "Error occured while deleting cluster"
409
+		return 0
410
+	fi
411
+}
412
+
413
+@when 'ibm-was-nd-dm.manager.started' 'was-ihs.available'
414
+@when_not 'ibm-was-nd-dm.ihs.exposed'
415
+function expose_was_nd_dm_ihs_details(){
416
+	juju-log "IBM WAS ND DM: ************************In expose_was_base_details function ***********"
417
+	was_path=$WAS_ND_INSTALL_PATH
418
+	was_port='9060'
419
+	host_name=`unit-get private-address`
420
+	services=$(relation_call --state=was-ihs.available services)
421
+	for service in $services; do
422
+		juju-log "IBM WAS ND DM: Creating the users for the IHS Admin Server"
423
+		get_cfgusername $service
424
+		juju-log "IBM WAS ND DM: Created IHS Admin Server Credentials."
425
+		juju-log "IBM WAS ND DM: Other vals are was_path = $was_path hostname = $host_name service = $service ihsadminuser = $ihsadminuser ihsadminpw = $ihsadminpw ihsadmingrp = $ihsadmingrp profilename = $profile_name"
426
+		relation_call --state=was-ihs.available set_was_details $service $was_path $was_port $host_name $ihsadminuser $ihsadmingrp $ihsadminpw $profile_name || true
427
+	done
428
+	set_state 'ibm-was-nd-dm.ihs.exposed'
429
+}
430
+
431
+@when 'was-ihs.available' 'ibm-was-nd-dm.manager.started'
432
+@when_not 'ibm-was-nd-dm.ihs.propagated'
433
+function propagate_ihs(){
434
+	juju-log "IBM WAS ND DM: ************************In propagate_ihs function ***********"
435
+	services=$(relation_call --state=was-ihs.available services)
436
+	for service in $services; do
437
+		juju-log "IBM WAS ND DM: Checking IHS Admin Server status."
438
+		ihsserverstatus=$(relation_call --state=was-ihs.available get_isihsstarted $service) || true
439
+		if [ "$ihsadminserverstatus" != "started" ] 
440
+		then
441
+			juju-log "IBM WAS ND DM: IHS admin server is not yet started."
442
+			continue
443
+		else
444
+				set_state 'ibm-was-nd-dm.ihs.propagated'
445
+				juju-log "IBM WAS ND DM: IHS propagated successfully!"
446
+				status-set active "IHS propagated successfully!"
447
+		fi
448
+	done
449
+}
450
+
451
+@when_not 'was-ihs.departed' 
452
+function remove_relation(){
453
+	juju-log "IBM WAS ND DM:Removing relation from IHS."
454
+	remove_state 'ibm-was-nd-dm.ihs.exposed'
455
+	remove_state 'ibm-was-nd-dm.ihs.propagated'
456
+	juju-log "IBM WAS ND DM: Removed relation from IHS"
457
+}
458
+
459
+reactive_handler_main
Back to file index

reactive/ibm-was-nd.sh

  1
--- 
  2
+++ reactive/ibm-was-nd.sh
  3
@@ -0,0 +1,135 @@
  4
+#!/bin/bash
  5
+set -ex
  6
+
  7
+source charms.reactive.sh
  8
+
  9
+reactive_states=`charms.reactive -y get_states`
 10
+
 11
+WAS_ND_INSTALL_PATH=/root/IBM/WebSphere/AppServer/V85/ND
 12
+IM_INSTALL_PATH=/opt/IBM/InstallationManager
 13
+ARCHITECTURE=`uname -m`
 14
+
 15
+if [ "$ARCHITECTURE" != "x86_64" -a  "$ARCHITECTURE" != "ppc64le" ]; then
 16
+	juju-log "IBM WAS ND: Unsupported platform. IBM WAS ND installed with this Charm supports only the x86_64 and ppc64le platforms."
 17
+	status-set blocked "IBM WAS ND: Unsupported architecture"
 18
+	exit 1
 19
+fi
 20
+
 21
+@when 'ibm-im.installed'
 22
+@when_not 'ibm-was-nd.installed'
 23
+function install_WAS_ND() {
 24
+	juju-log "IBM WAS ND: Checking etc/hosts file."
 25
+	private_address=`unit-get private-address`
 26
+	juju-log "private_address : $private_address"
 27
+	cd /etc
 28
+	if grep -q "$private_address" hosts
 29
+	then
 30
+		juju-log "IBM WAS ND: Host file already updated"
 31
+	else
 32
+		juju-log "IBM WAS ND: Updating Host file"
 33
+		echo "$private_address `hostname`" >> /etc/hosts
 34
+	fi
 35
+
 36
+	# Get the installable WAS ND resources
 37
+	juju-log "IBM WAS ND: fetching the ibm_was_nd_installer1 resource"
 38
+	status-set maintenance "fetching the ibm_was_nd_installer1 resource"
 39
+	cfg_was_nd_pkg1_name=`resource-get 'ibm_was_nd_installer1' || echo unavailable`
 40
+	
 41
+	juju-log "IBM WAS ND: fetching the ibm_was_nd_installer2 resource"
 42
+	status-set maintenance "fetching the ibm_was_nd_installer2 resource"
 43
+	cfg_was_nd_pkg2_name=`resource-get 'ibm_was_nd_installer2' || echo unavailable`
 44
+	
 45
+	ibm_was_pkg1_isempty=`file $cfg_was_nd_pkg1_name | { grep -q empty && echo "True"; } || echo "False"`
 46
+	ibm_was_pkg2_isempty=`file $cfg_was_nd_pkg2_name | { grep -q empty && echo "True"; } || echo "False"`
 47
+	if [ "$ARCHITECTURE" == "x86_64" ];then
 48
+		juju-log "IBM WAS ND: fetching the ibm_was_nd_installer3 resource"
 49
+		status-set maintenance "fetching the ibm_was_nd_installer3 resource"
 50
+		cfg_was_nd_pkg3_name=`resource-get 'ibm_was_nd_installer3' || echo unavailable`
 51
+		ibm_was_pkg3_isempty=`file $cfg_was_nd_pkg3_name | { grep -q empty && echo "True"; } || echo "False"`	  
 52
+		# If we don't have a package, report blocked status; we can't proceed.
 53
+		if [ "$cfg_was_nd_pkg1_name" = "unavailable" -o "$cfg_was_nd_pkg2_name" = "unavailable" -o "$cfg_was_nd_pkg3_name" = "unavailable" ]; then
 54
+			juju-log "IBM WAS ND: missing required ibm_was_nd_installer resource"
 55
+			status-set blocked "missing required ibm_was_nd_installer resource"
 56
+			return 0
 57
+		fi
 58
+		
 59
+		if [ "$ibm_was_pkg1_isempty" = "True" -o "$ibm_was_pkg2_isempty" = "True" -o "$ibm_was_pkg3_isempty" = "True" ]; then
 60
+			juju-log "IBM WAS ND: missing required ibm_was_nd_installer resource, found empty packages"
 61
+			status-set blocked "missing required ibm_was_nd_installer resource, provide actual ibm_was_nd_installer resources."
 62
+			return 0
 63
+		fi
 64
+		
 65
+	elif [ "$ARCHITECTURE" == "ppc64le" ];then
 66
+		# If we don't have a package, report blocked status; we can't proceed.
 67
+		if [ "$cfg_was_nd_pkg1_name" = "unavailable" -o "$cfg_was_nd_pkg2_name" = "unavailable" ]; then
 68
+			juju-log "IBM WAS ND: missing required ibm_was_nd_installer resource"
 69
+			status-set blocked "missing required ibm_was_nd_installer resource"
 70
+			return 0
 71
+		fi
 72
+		if [ "$ibm_was_pkg1_isempty" = "True" -o "$ibm_was_pkg2_isempty" = "True" ]; then
 73
+			juju-log "IBM WAS ND: missing required ibm_was_nd_installer resource, found empty packages"
 74
+			status-set blocked "missing required ibm_was_nd_installer resource, provide actual ibm_was_nd_installer resources."
 75
+			return 0
 76
+		fi
 77
+	fi
 78
+	
 79
+	juju-log "IBM WAS ND: using $cfg_was_nd_pkg1_name $cfg_was_nd_pkg2_name $cfg_was_nd_pkg3_name as the ibm_was_nd_installer resources"
 80
+	was_nd_installers="$cfg_was_nd_pkg1_name $cfg_was_nd_pkg2_name $cfg_was_nd_pkg3_name"
 81
+	juju-log "IBM WAS ND: was_nd_installers===$was_nd_installers"
 82
+	
 83
+	for was_pkg in $was_nd_installers; do
 84
+		juju-log "IBM WAS ND: was_pkg====="$was_pkg
 85
+		if [ -f $was_pkg ]; then
 86
+			ARCHIVE_DIR=`dirname $was_pkg`
 87
+			juju-log "IBM WAS ND: extracting the ibm_was_nd_installer resources"
 88
+			status-set maintenance "extracting the ibm_was_nd_installer resource"
 89
+			if [ -d $ARCHIVE_DIR/../WAS_ND/disk1 -a -d $ARCHIVE_DIR/../WAS_ND/disk2 -a -d $ARCHIVE_DIR/../WAS_ND/disk3 ];then
 90
+				juju-log "IBM WAS ND: ibm_was_nd_installer resources already extracted"
 91
+			else
 92
+				if [ ! -d $ARCHIVE_DIR/../WAS_ND ]; then
 93
+					mkdir $ARCHIVE_DIR/../WAS_ND
 94
+				fi				
 95
+				if ! unzip $was_pkg -d $ARCHIVE_DIR/../WAS_ND
 96
+				then
 97
+					juju-log "IBM WAS ND: Unable to extract the WAS ND packages content. Verify whether the package is corrupt."
 98
+					# Remove corrupt archive file
 99
+					status-set blocked "IBM WAS ND packages are corrupt"
100
+					rm -rf $ARCHIVE_DIR/../WAS_ND 
101
+					return 0
102
+				else
103
+					juju-log "IBM WAS ND: $was_pkg extracted successfully"
104
+				fi
105
+			fi
106
+		fi
107
+	done
108
+	
109
+	juju-log "IBM WAS ND: starting WAS ND installation."
110
+	status-set maintenance "IBM WAS ND: Installing WAS ND"
111
+	#sh does not work in ubuntu so linking to bash
112
+	juju-log "IBM WAS ND: Unlink /bin/sh"
113
+	unlink /bin/sh
114
+	ln -s /bin/bash /bin/sh
115
+	if [ "$ARCHITECTURE" == "x86_64" ];then
116
+		if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.ND.v85 -acceptLicense -repositories $ARCHIVE_DIR/../WAS_ND -installationDirectory $WAS_ND_INSTALL_PATH  -showProgress
117
+		then
118
+			set_state 'ibm-was-nd.installed'
119
+			status-set active "IBM WAS ND: WAS ND is installed successfully!"
120
+		else
121
+			status-set blocked "IBM WAS ND: WAS ND Installation failed!"
122
+			return 1
123
+		fi
124
+                          
125
+	elif [ "$ARCHITECTURE" == "ppc64le" ];then
126
+		if $IM_INSTALL_PATH/eclipse/tools/imcl install com.ibm.websphere.ND.le.v85 -acceptLicense -repositories $ARCHIVE_DIR/../WAS_ND -installationDirectory $WAS_ND_INSTALL_PATH  -showProgress
127
+		then
128
+			set_state 'ibm-was-nd.installed'
129
+			status-set active "IBM WAS ND: WAS ND is installed successfully!"
130
+		else
131
+			status-set blocked "IBM WAS ND: WAS ND Installation failed!"
132
+			return 1
133
+		fi
134
+	fi
135
+              
136
+}
137
+
138
+reactive_handler_main
Back to file index

reactive/leadership.py

 1
--- 
 2
+++ reactive/leadership.py
 3
@@ -0,0 +1,68 @@
 4
+# Copyright 2015-2016 Canonical Ltd.
 5
+#
 6
+# This file is part of the Leadership Layer for Juju.
 7
+#
 8
+# This program is free software: you can redistribute it and/or modify
 9
+# it under the terms of the GNU General Public License version 3, as
10
+# published by the Free Software Foundation.
11
+#
12
+# This program is distributed in the hope that it will be useful, but
13
+# WITHOUT ANY WARRANTY; without even the implied warranties of
14
+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15
+# PURPOSE.  See the GNU General Public License for more details.
16
+#
17
+# You should have received a copy of the GNU General Public License
18
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
+
20
+from charmhelpers.core import hookenv
21
+from charmhelpers.core import unitdata
22
+
23
+from charms import reactive
24
+from charms.leadership import leader_get, leader_set
25
+
26
+
27
+__all__ = ['leader_get', 'leader_set']  # Backwards compatibility
28
+
29
+
30
+def initialize_leadership_state():
31
+    '''Initialize leadership.* states from the hook environment.
32
+
33
+    Invoked by hookenv.atstart() so states are available in
34
+    @hook decorated handlers.
35
+    '''
36
+    is_leader = hookenv.is_leader()
37
+    if is_leader:
38
+        hookenv.log('Initializing Leadership Layer (is leader)')
39
+    else:
40
+        hookenv.log('Initializing Leadership Layer (is follower)')
41
+
42
+    reactive.helpers.toggle_state('leadership.is_leader', is_leader)
43
+
44
+    previous = unitdata.kv().getrange('leadership.settings.', strip=True)
45
+    current = hookenv.leader_get()
46
+
47
+    # Handle deletions.
48
+    for key in set(previous.keys()) - set(current.keys()):
49
+        current[key] = None
50
+
51
+    any_changed = False
52
+    for key, value in current.items():
53
+        reactive.helpers.toggle_state('leadership.changed.{}'.format(key),
54
+                                      value != previous.get(key))
55
+        if value != previous.get(key):
56
+            any_changed = True
57
+        reactive.helpers.toggle_state('leadership.set.{}'.format(key),
58
+                                      value is not None)
59
+    reactive.helpers.toggle_state('leadership.changed', any_changed)
60
+
61
+    unitdata.kv().update(current, prefix='leadership.settings.')
62
+
63
+
64
+# Per https://github.com/juju-solutions/charms.reactive/issues/33,
65
+# this module may be imported multiple times so ensure the
66
+# initialization hook is only registered once. I have to piggy back
67
+# onto the namespace of a module imported before reactive discovery
68
+# to do this.
69
+if not hasattr(reactive, '_leadership_registered'):
70
+    hookenv.atstart(initialize_leadership_state)
71
+    reactive._leadership_registered = True
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,54 @@
 4
+#!/usr/bin/env python3
 5
+
 6
+
 7
+import amulet
 8
+import re
 9
+import requests
10
+import unittest
11
+
12
+seconds_to_wait = 20000
13
+
14
+
15
+class TestDeploy(unittest.TestCase):
16
+    """
17
+    Deployment test for the IBM WAS ND DM charm.
18
+    """
19
+    def setUp(self):
20
+        self.d = amulet.Deployment(series='trusty')
21
+        self.d.add('ibm-was-base', 'cs:~ibmcharmers/trusty/ibm-was-nd-dm')
22
+        self.d.add('ibm-http', 'cs:~ibmcharmers/trusty/ibm-http')
23
+        self.d.relate('ibm-was-nd-dm:was-ihs', 'ibm-http:ihs')
24
+        self.d.setup(seconds_to_wait)
25
+        self.d.sentry.wait(seconds_to_wait)
26
+
27
+    def test_deploy_with_placeholder_resource(self):
28
+        # The status message when using placeholder resources will include the
29
+        # string "ibm_was_nd_installer resource". If we see that, the test is
30
+        # successful.
31
+        sentry_re = re.compile('ibm_was_nd_installer resource')
32
+        self.d.sentry.wait_for_messages({"ibm-was-nd": sentry_re})
33
+
34
+    def test_was_nd_dm_deployed(self):
35
+        self.assertTrue(self.d.deployed)
36
+        unit = self.d.sentry['ibm-was-nd-dm'][0]
37
+        state_was_nd_dm = unit.info['agent-state']
38
+        print('WAS ND DM is %s' % state_was_nd_dm)
39
+        url = 'http://%s:9060/ibm/console' % unit.info['public-address']
40
+        https_url = 'https://%s:9043/ibm/console' % unit.info['public-address']
41
+        response = requests.get(url, verify=False)
42
+        response = requests.get(https_url, verify=False)
43
+        # Raise an exception if the url was not a valid web page.
44
+        response.raise_for_status()
45
+
46
+    def test_was_nd_dm_ihs(self):
47
+        http_unit = self.d.sentry['ibm-http'][0]
48
+        state_http = http_unit.info['agent-state']
49
+        print('Http Server is %s' % state_http)
50
+        url = 'http://%s:80' % http_unit.info['public-address']
51
+        snoop_url = 'http://%s:80/snoop' % http_unit.info['public-address']
52
+        response = requests.get(url, verify=False)
53
+        response = requests.get(snoop_url, verify=False)
54
+        response.raise_for_status()
55
+
56
+if __name__ == '__main__':
57
+    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