Merge lp:~ibmcharmers/charms/trusty/ibm-mq/trunk into lp:charms/trusty/ibm-mq
- Trusty Tahr (14.04)
- trunk
- Merge into trunk
Proposed by
Anita Nayak
Status: | Needs review | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~ibmcharmers/charms/trusty/ibm-mq/trunk | ||||
Merge into: | lp:charms/trusty/ibm-mq | ||||
Diff against target: |
4832 lines (+3632/-478) 83 files modified
Makefile (+24/-0) README.md (+114/-127) bin/layer_option (+24/-0) config.yaml (+0/-12) copyright (+1/-1) deps/layer/layer-apt/.gitignore (+2/-0) deps/layer/layer-apt/README.md (+195/-0) deps/layer/layer-apt/config.yaml (+34/-0) deps/layer/layer-apt/copyright (+15/-0) deps/layer/layer-apt/layer.yaml (+5/-0) deps/layer/layer-apt/lib/charms/__init__.py (+2/-0) deps/layer/layer-apt/lib/charms/apt.py (+182/-0) deps/layer/layer-apt/reactive/apt.py (+131/-0) deps/layer/layer-basic/.gitignore (+5/-0) deps/layer/layer-basic/Makefile (+24/-0) deps/layer/layer-basic/README.md (+221/-0) deps/layer/layer-basic/bin/layer_option (+24/-0) deps/layer/layer-basic/copyright (+9/-0) deps/layer/layer-basic/hooks/config-changed (+19/-0) deps/layer/layer-basic/hooks/hook.template (+19/-0) deps/layer/layer-basic/hooks/install (+19/-0) deps/layer/layer-basic/hooks/leader-elected (+19/-0) deps/layer/layer-basic/hooks/leader-settings-changed (+19/-0) deps/layer/layer-basic/hooks/start (+19/-0) deps/layer/layer-basic/hooks/stop (+19/-0) deps/layer/layer-basic/hooks/update-status (+19/-0) deps/layer/layer-basic/hooks/upgrade-charm (+28/-0) deps/layer/layer-basic/layer.yaml (+18/-0) deps/layer/layer-basic/lib/charms/layer/__init__.py (+21/-0) deps/layer/layer-basic/lib/charms/layer/basic.py (+159/-0) deps/layer/layer-basic/lib/charms/layer/execd.py (+138/-0) deps/layer/layer-basic/metadata.yaml (+1/-0) deps/layer/layer-basic/requirements.txt (+2/-0) deps/layer/layer-basic/tox.ini (+12/-0) deps/layer/layer-basic/wheelhouse.txt (+3/-0) deps/layer/layer-leadership/.gitignore (+2/-0) deps/layer/layer-leadership/README.md (+88/-0) deps/layer/layer-leadership/copyright (+15/-0) deps/layer/layer-leadership/layer.yaml (+17/-0) deps/layer/layer-leadership/lib/charms/__init__.py (+2/-0) deps/layer/layer-leadership/lib/charms/leadership.py (+58/-0) deps/layer/layer-leadership/reactive/leadership.py (+68/-0) deps/layer/trunk/README.md (+57/-0) deps/layer/trunk/config.yaml (+29/-0) deps/layer/trunk/layer.yaml (+9/-0) deps/layer/trunk/metadata.yaml (+15/-0) deps/layer/trunk/reactive/ibm-base.sh (+107/-0) hooks/config-changed (+0/-242) hooks/hook.template (+19/-0) hooks/install (+0/-30) hooks/leader-elected (+19/-0) hooks/leader-settings-changed (+19/-0) hooks/messaging-relation-broken (+19/-0) hooks/messaging-relation-changed (+19/-0) hooks/messaging-relation-departed (+19/-0) hooks/messaging-relation-joined (+0/-12) hooks/relations/ibm-mq/README.md (+42/-0) hooks/relations/ibm-mq/interface.yaml (+4/-0) hooks/relations/ibm-mq/provides.py (+50/-0) hooks/relations/ibm-mq/requires.py (+42/-0) hooks/start (+0/-8) hooks/stop (+0/-6) hooks/update-status (+19/-0) hooks/upgrade-charm (+28/-0) layer.yaml (+21/-0) lib/charms/__init__.py (+2/-0) lib/charms/apt.py (+182/-0) lib/charms/layer/__init__.py (+21/-0) lib/charms/layer/basic.py (+159/-0) lib/charms/layer/execd.py (+138/-0) lib/charms/leadership.py (+58/-0) metadata.yaml (+19/-14) reactive/apt.py (+131/-0) reactive/ibm-base.sh (+107/-0) reactive/ibm-mq.sh (+340/-0) reactive/leadership.py (+68/-0) reactive/test (+1/-0) requirements.txt (+2/-0) revision (+1/-1) tests/00-setup (+13/-1) tests/10-bundles-test.py (+45/-14) tests/bundles.yaml (+0/-10) tox.ini (+12/-0) |
||||
To merge this branch: | bzr merge lp:~ibmcharmers/charms/trusty/ibm-mq/trunk | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Matt Bruzek | Pending | ||
Review via email: mp+307673@code.launchpad.net |
Commit message
Description of the change
Hi Matt,
I have updated ibm-mq charm as per below:
1. updated /etc/pam.
2. Updated the README.md appropriately.
To post a comment you must log in.
- 53. By Anita Nayak
-
checkin after review comment incorporation to interface interface-ibm-mq
Unmerged revisions
- 53. By Anita Nayak
-
checkin after review comment incorporation to interface interface-ibm-mq
- 52. By Anita Nayak
-
Check in for trusty version of ibm-mq
- 51. By Anita Nayak
-
check-in with proper file permission
- 50. By Anita Nayak
-
Check-in for ibm-mq layered for trusty
- 49. By krishna bandi
-
updated ibm-mq charm
- 48. By krishna bandi
-
Updated Amulet test files
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file 'Makefile' |
2 | --- Makefile 1970-01-01 00:00:00 +0000 |
3 | +++ Makefile 2017-02-15 08:51:47 +0000 |
4 | @@ -0,0 +1,24 @@ |
5 | +#!/usr/bin/make |
6 | + |
7 | +all: lint unit_test |
8 | + |
9 | + |
10 | +.PHONY: clean |
11 | +clean: |
12 | + @rm -rf .tox |
13 | + |
14 | +.PHONY: apt_prereqs |
15 | +apt_prereqs: |
16 | + @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip) |
17 | + @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox) |
18 | + |
19 | +.PHONY: lint |
20 | +lint: apt_prereqs |
21 | + @tox --notest |
22 | + @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests) |
23 | + @charm proof |
24 | + |
25 | +.PHONY: unit_test |
26 | +unit_test: apt_prereqs |
27 | + @echo Starting tests... |
28 | + tox |
29 | |
30 | === modified file 'README.md' |
31 | --- README.md 2015-07-09 18:38:01 +0000 |
32 | +++ README.md 2017-02-15 08:51:47 +0000 |
33 | @@ -1,157 +1,144 @@ |
34 | - |
35 | -Charm for IBM MQ |
36 | - |
37 | -Overview |
38 | --------- |
39 | +## Charm for IBM MQ |
40 | + |
41 | +### Overview |
42 | |
43 | IBM MQ |
44 | |
45 | -IBM MQ provides for messaging services to transport multiple types of data. |
46 | -MQ Runtime, Server and Sample components from IBM MQ product |
47 | -can be deployed using this charm. |
48 | +IBM MQ provides messaging services to transport multiple types of data. |
49 | +MQ Runtime, Server and Sample components from IBM MQ product can be deployed using this charm. |
50 | |
51 | |
52 | This charm installs IBM MQ product and configures it's IP Address and default |
53 | port number (1414). MQ Administrators can setup queue managers |
54 | and queues. This will allow IBM MQ client application to transmit/receive messages. |
55 | |
56 | -More information on IBM MQ available at the [Product Page][product-page] |
57 | -and [IBM Knowledge Center][mq-v8-info] |
58 | - |
59 | -Usage |
60 | ------ |
61 | - |
62 | -Download (see below) your licensed IBM MQ 8.0.0.2 software and copy it to the |
63 | -`files/archives` directory of MQ charm. |
64 | - |
65 | -For e.g: |
66 | - |
67 | - * Go to the `files/archives` directory. |
68 | - |
69 | - * Copy the `WS_MQ_8.0.0.2_FOR_LIN_ON_LE_POWER.tar.gz` file to this directory for |
70 | - `POWER LE` base machines. For `x86_64`, use `WS_MQ_V8.0.0.2_LINUX_ON_X86_64_I.tar.gz`. |
71 | - |
72 | - * The file name you have could be different from the example above (but with the |
73 | - `.gz` extension). Ensure a backup copy of the file. |
74 | - |
75 | -To install the downloaded binaries you must agree to the IBM license. The license |
76 | -file(s) can be found under the `files/archives/licenses` directory on extraction |
77 | -of IBM MQ software using the following command: |
78 | - |
79 | - tar xvfz *.gz --strip-components=1 |
80 | - |
81 | -The license can also be viewed by running the following command after extracting |
82 | -IBM MQ to the files/archives directory: |
83 | - |
84 | - ./mqlicense.sh -text_only |
85 | - |
86 | -Run the following to deploy this charm: |
87 | - |
88 | +More information on IBM MQ available at the [Product Page](http://www-03.ibm.com/software/products/en/ibm-mq) and [IBM Knowledge Center](http://www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.helphome.v80.doc/WelcomePagev8r0.htm) |
89 | + |
90 | +### Usage |
91 | + |
92 | +Download your licensed IBM MQ software and host it in a way that can be downloaded using curl. |
93 | +curl-url and curl-opts are two main options we are using from ibm-base-layer to download the ibm product as IBM-MQ is built on top of the ibm-base layer [More Info](http://interfaces.juju.solutions/layer/ibm-base/) |
94 | + |
95 | +### Deploy |
96 | + |
97 | +Run the following commands to deploy this charm: |
98 | + |
99 | juju deploy ibm-mq |
100 | |
101 | -At this point the charm will wait until the user accepts the license. If you |
102 | -agree to the license, run the following command: |
103 | - |
104 | - juju set ibm-mq accept-ibm-mq-license=True |
105 | +Here the charm will wait until the user accepts the license. If you agree to the license, run the following command. |
106 | + |
107 | + juju set-config ibm-mq license_accepted="True" |
108 | + |
109 | +IBM MQ charm also needs an host from where the packages can be downloaded (the user has to keep the MQ packages on this host server), from the directory where the package is kept, user name and password to connect to the host server (as mentioned in the creating repositories section). |
110 | + |
111 | +We have to provide values for curl-url and curl-opt to download the product. |
112 | +curl-url with host name,package_dir, package name, checksum value, curl-opts with user name and password, to proceed with the installation. To provide these run the following command: |
113 | + |
114 | + juju set-config ibm-mq curl_url="<hostname>/<package_dir><package_name>?<sha512=value>" |
115 | + juju set-config ibm-mq curl_opts="-u <username>:<password>" |
116 | + |
117 | + |
118 | +For eg: |
119 | + juju set-config ibm-mq curl_url="http://1.2.3.4/debs/MQ/WS_MQ_V8.0.0.2_LINUX_ON_X86_64_I.tar.gz?sha512=cd604a577ae51d93fc384241ce2faf21927b202d0a9e5f9b9a25caf4460fd6c34f32649caef8ecd543c244413788fe105987d16553e2d4754ef02d924843e95f" |
120 | + |
121 | + juju set-config ibm-mq curl_opts="-u root:root123" |
122 | + |
123 | +Setting the license-accepted to False will uninstall the product: |
124 | + |
125 | + juju set-config ibm-mq license_accepted="False" |
126 | + |
127 | + |
128 | +### Update the following system configuration manually: |
129 | |
130 | In order to create MQ objects like queue, it is required that some |
131 | -of the system configurations be updated. Details provided at |
132 | -[IBM Knowledge Center][mq-config-info]. |
133 | - |
134 | -The Charm updates the following system configuration: |
135 | + of the system configurations be updated. Details provided at |
136 | + [IBM Knowledge Center][mq-config-info]. |
137 | |
138 | * In `/etc/sysctl.conf`: |
139 | |
140 | fs.file-max = 524288 |
141 | kernel.shmmax = 268435456 |
142 | |
143 | - * In `/etc/security/limits.conf`: |
144 | +### Relation with other services: |
145 | + |
146 | + ibm-mq can have relation with other services to get the service specific queue manager. Per example if ibm-mq gets relation with ibm-was then, ibm-was specific queue manger will be created like 'ibm-wasQM'. |
147 | + |
148 | + juju add-relation ibm-mq:messaging ibm-was-base:wasmessaging |
149 | + or |
150 | + |
151 | + juju add-relation ibm-mq ibm-was-base |
152 | + |
153 | +IBM-MQ sets Queue Manager name, Queue Name, IP Address and Port for the connected services. |
154 | + It creates a Queue Manager for the consumer application and a local queue i.e QUEUE1 for above Queue Manager. |
155 | + But as a Queue Name it sets a filename where MQSC Commands can be edited for further operation. |
156 | + |
157 | +on removal of relation with the service with which already relation was established, the service specific queue manger will be removed i.e in the above case 'ibm-wasQM' will be removed. |
158 | + |
159 | + juju remove-relation ibm-mq:messaging ibm-was-base:wasmessaging |
160 | +or |
161 | + |
162 | + juju remove-relation ibm-mq ibm-was-base |
163 | +##### The Charm updates the following system configuration: |
164 | + |
165 | +In order to create MQ objects like queue, it is required that some |
166 | +of the system configurations be updated. Details provided at |
167 | +[IBM Knowledge Center][mq-config-info]. |
168 | + |
169 | + * Adds user `ubuntu` to group `mqm`. |
170 | + |
171 | + * In `/etc/security/limits.conf` if hard nofile and soft nofile limit for user `mqm` are less than 10240: |
172 | |
173 | mqm hard nofile 10240 |
174 | mqm soft nofile 10240 |
175 | - |
176 | - * Adds user `ubuntu` to group `mqm`. |
177 | + |
178 | + * Updates `/etc/pam.d/common-session` file to take the above configurations to take effect |
179 | |
180 | Verify these changes have been made successfully if MQ throws up any errors. |
181 | |
182 | -Configuration |
183 | -------------- |
184 | - |
185 | -`accept-ibm-mq-license` |
186 | -Before you can use or install IBM MQ, |
187 | -you must accept the terms of International License |
188 | -Agreement for Non-Warranted Programs and additional license information. |
189 | -Please read the license agreement carefully. |
190 | - |
191 | -The license file(s) can be found under the `files/archives` directory after |
192 | -downloading and extracting IBM MQ there. The license can also be viewed by |
193 | -running the following command after extracting IBM MQ to the `files/archives` |
194 | -directory: |
195 | - ./mqlicense.sh -text_only |
196 | - |
197 | -Set the value of accept-ibm-mq-license to True if you agree to IBM MQ license. |
198 | - |
199 | -**The IBM MQ software cannot be installed until the terms and |
200 | -conditions are accepted. The charm will not function correctly until the |
201 | -this configuration option is set to True.** |
202 | - |
203 | -Known Limitations |
204 | ------------------ |
205 | + |
206 | +#### Configuration |
207 | + |
208 | +**license_accepted** (boolean) Some IBM charms require acceptance of a license before installation can proceed. If required, setting this option to True indicates that you have read and accepted the IBM terms and conditions found in the license file referenced by the charm. |
209 | + |
210 | + **curl_url** |
211 | + |
212 | + For downloading package we need to set curl_url with following options. |
213 | + - *hostname* - The web server host name from which IBM MQ installation packages can be downloaded. |
214 | + - *package_dir* - The package directory path in the web server. |
215 | + - *package name* - The IBM MQ Package name. |
216 | + - *checksum value* - Checksum value to check integrity of IBM MQ package. The Charm |
217 | + uses sha512sum to check the integrity. If empty, it does not carry out the |
218 | + integrity check. Use `sha512sum <Package Location/Package Name>` |
219 | + to find out Checksum value for downloaded package. |
220 | + |
221 | +**curl_opts** |
222 | + |
223 | + We need to set curl_opts with following options. - username - User name of the webserver/sftp host. - password - Password of the webserver/sftp host. |
224 | + |
225 | +##### Known Limitations: |
226 | |
227 | Local providers does not allow updates to system configuration using sysctl |
228 | command. These configurations will need be done manually for MQ to function. |
229 | The required configurations are provided in this document. |
230 | |
231 | -IBM MQ Information |
232 | -------------------- |
233 | - |
234 | -### (1) General Information |
235 | - |
236 | -Details about MQ available [here][mq-info]. |
237 | - |
238 | -Details about MQ Version 8.0 available at [IBM Knowledge Center][mq-v8-info]. |
239 | - |
240 | -### (2) Download Information |
241 | - |
242 | -Information on procuring MQ product is available at the [Product Page][product-page] |
243 | -and at the [Passport Advantage Site][passport]. |
244 | - |
245 | -A development version for `x86_64` is available [here][dev-download]. |
246 | -After clicking on the above link and if you agree to the license, click on |
247 | -'I agree' link to download the IBM MQ package. |
248 | - |
249 | -A 90 days trial version is available [here][trial-download]. |
250 | - |
251 | -### (3) License Information |
252 | - |
253 | -License information for IBM MQ is available as part of the downloaded product |
254 | -package. The license can be viewed by running the following command after |
255 | -extracting IBM MQ: |
256 | - |
257 | - ./mqlicense.sh -text_only |
258 | - |
259 | -License for the development version is available |
260 | -[here][license]. |
261 | - |
262 | -# Contact Information |
263 | - |
264 | -For issues with this charm, please contact <jujusupp@us.ibm.com> |
265 | - |
266 | - |
267 | -<!-- Links --> |
268 | - |
269 | -[mq-v8-info]: http://www-01.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.helphome.v80.doc/WelcomePagev8r0.htm "MQ Version 8.0" |
270 | - |
271 | -[product-page]: http://www-03.ibm.com/software/products/en/ibm-mq "MQ Product Page" |
272 | - |
273 | -[mq-config-info]: http://www-01.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.ins.doc/q008550_.htm?lang=en |
274 | - |
275 | -[mq-info]: http://www-01.ibm.com/software/integration/wmq/library/ "MQ Info" |
276 | - |
277 | -[passport]: http://www-01.ibm.com/software/passportadvantage/ "Passport Advantage" |
278 | - |
279 | -[dev-download]: http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?popup=Y&li_formnum=L-APIG-9BUHAE&accepted_url=http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev80_linux_x86-64.tar.gz "x86_64 Dev Download" |
280 | - |
281 | -[trial-download]: http://www-03.ibm.com/software/products/en/ibm-mq "Trial Download" |
282 | - |
283 | -[license]: http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?popup=Y&li_formnum=L-APIG-9BUHAE&accepted_url=http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev80_linux_x86-64.tar.gz "License" |
284 | +### IBM MQ Information |
285 | + |
286 | +(1) General Information |
287 | +Details about MQ available [here](http://www-01.ibm.com/software/integration/wmq/library/). |
288 | +Details about MQ Version 8.0 available at [IBM Knowledge Center](http://www.ibm.com/support/knowledgecenter/SSFKSJ_8.0.0/com.ibm.mq.helphome.v80.doc/WelcomePagev8r0.htm). |
289 | + |
290 | +(2) Download Information |
291 | +Information on procuring MQ product is available at the [Product Page](http://www-03.ibm.com/software/products/en/ibm-mq) and at the [Passport Advantage Site](http://www-01.ibm.com/software/passportadvantage/). |
292 | + |
293 | +A development version for x86_64 is available [here](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?popup=Y&li_formnum=L-APIG-9BUHAE&accepted_url=http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev80_linux_x86-64.tar.gz). After clicking on the above link and if you agree to the license, click on 'I agree' link to download the IBM MQ package. |
294 | +A 90 days trial version is available [here](http://www-03.ibm.com/software/products/en/ibm-mq). |
295 | + |
296 | +(3) License Information |
297 | +License information for IBM MQ is available as part of the downloaded product package. The license can be viewed by running the following command after extracting IBM MQ. |
298 | + |
299 | +./mqlicense.sh -text_only |
300 | + |
301 | +License for the development version is available [here](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?popup=Y&li_formnum=L-APIG-9BUHAE&accepted_url=http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev80_linux_x86-64.tar.gz). |
302 | + |
303 | +Contact Information |
304 | +For issues with this charm, please contact jujusupp@us.ibm.com |
305 | |
306 | === added directory 'bin' |
307 | === added file 'bin/layer_option' |
308 | --- bin/layer_option 1970-01-01 00:00:00 +0000 |
309 | +++ bin/layer_option 2017-02-15 08:51:47 +0000 |
310 | @@ -0,0 +1,24 @@ |
311 | +#!/usr/bin/env python3 |
312 | + |
313 | +import sys |
314 | +sys.path.append('lib') |
315 | + |
316 | +import argparse |
317 | +from charms.layer import options |
318 | + |
319 | + |
320 | +parser = argparse.ArgumentParser(description='Access layer options.') |
321 | +parser.add_argument('section', |
322 | + help='the section, or layer, the option is from') |
323 | +parser.add_argument('option', |
324 | + help='the option to access') |
325 | + |
326 | +args = parser.parse_args() |
327 | +value = options(args.section).get(args.option, '') |
328 | +if isinstance(value, bool): |
329 | + sys.exit(0 if value else 1) |
330 | +elif isinstance(value, list): |
331 | + for val in value: |
332 | + print(val) |
333 | +else: |
334 | + print(value) |
335 | |
336 | === added file 'config.yaml' |
337 | --- config.yaml 1970-01-01 00:00:00 +0000 |
338 | +++ config.yaml 2017-02-15 08:51:47 +0000 |
339 | @@ -0,0 +1,55 @@ |
340 | +"options": |
341 | + "extra_packages": |
342 | + "description": "Space separated list of extra deb packages to install.\n" |
343 | + "type": "string" |
344 | + "default": "" |
345 | + "package_status": |
346 | + "default": "install" |
347 | + "type": "string" |
348 | + "description": "The status of service-affecting packages will be set to this value\ |
349 | + \ in the dpkg database. Valid values are \"install\" and \"hold\".\n" |
350 | + "install_sources": |
351 | + "description": "List of extra apt sources, per charm-helpers standard format (a\ |
352 | + \ yaml list of strings encoded as a string). Each source may be either a line\ |
353 | + \ that can be added directly to sources.list(5), or in the form ppa:<user>/<ppa-name>\ |
354 | + \ for adding Personal Package Archives, or a distribution component to enable.\n" |
355 | + "type": "string" |
356 | + "default": "" |
357 | + "install_keys": |
358 | + "description": "List of signing keys for install_sources package sources, per\ |
359 | + \ charmhelpers standard format (a yaml list of strings encoded as a string).\ |
360 | + \ The keys should be the full ASCII armoured GPG public keys. While GPG key\ |
361 | + \ ids are also supported and looked up on a keyserver, operators should be aware\ |
362 | + \ that this mechanism is insecure. null can be used if a standard package signing\ |
363 | + \ key is used that will already be installed on the machine, and for PPA sources\ |
364 | + \ where the package signing key is securely retrieved from Launchpad.\n" |
365 | + "type": "string" |
366 | + "default": "" |
367 | + "curl_url": |
368 | + "type": "string" |
369 | + "default": "" |
370 | + "description": | |
371 | + Location of the IBM product installation file(s). This should be a URL |
372 | + that curl can use to download files. Multiple URLs should be separated |
373 | + by a space. NOTE: cryptographic verification is required and must be |
374 | + specified as part of the URL query string with the key a valid hash |
375 | + algorithms md5, sha256, or sha512, and the the checksum value itself |
376 | + (http://<url>?[md5|sha256|sha512]=<checksum>). |
377 | + For example: |
378 | + 'http://example.com/file.tgz?sha256=<sum>' |
379 | + 'sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>' |
380 | + "curl_opts": |
381 | + "type": "string" |
382 | + "default": "" |
383 | + "description": | |
384 | + The options passed to the 'curl' command when fetching files from |
385 | + curl_url. For example: |
386 | + '-u <user:password>' |
387 | + "license_accepted": |
388 | + "type": "boolean" |
389 | + "default": !!bool "false" |
390 | + "description": | |
391 | + Some IBM charms require acceptance of a license before installation |
392 | + can proceed. If required, setting this option to True indicates that you |
393 | + have read and accepted the IBM terms and conditions found in the license |
394 | + file referenced by the charm. |
395 | |
396 | === removed file 'config.yaml' |
397 | --- config.yaml 2015-05-03 16:45:29 +0000 |
398 | +++ config.yaml 1970-01-01 00:00:00 +0000 |
399 | @@ -1,12 +0,0 @@ |
400 | -options: |
401 | - accept-ibm-mq-license: |
402 | - type: boolean |
403 | - default: False |
404 | - description: | |
405 | - The MQ software can only be used by |
406 | - accepting the terms and conditions for IBM MQ License. |
407 | - Setting this option to True |
408 | - indicates that you have read and accepted the IBM MQ terms and |
409 | - conditions found in the license file. The details about accessing |
410 | - the license(s) can be found in the README. |
411 | - ** Do not set to False if a relation exists with this charm ** |
412 | |
413 | === modified file 'copyright' |
414 | --- copyright 2015-03-11 19:52:30 +0000 |
415 | +++ copyright 2017-02-15 08:51:47 +0000 |
416 | @@ -1,4 +1,4 @@ |
417 | -Copyright 2015 IBM Corporation |
418 | +Copyright 2016 IBM Corporation |
419 | |
420 | This Charm is licensed under the Apache License, Version 2.0 (the "License"); |
421 | you may not use this file except in compliance with the License. |
422 | |
423 | === added directory 'deps' |
424 | === added directory 'deps/layer' |
425 | === added directory 'deps/layer/layer-apt' |
426 | === added file 'deps/layer/layer-apt/.gitignore' |
427 | --- deps/layer/layer-apt/.gitignore 1970-01-01 00:00:00 +0000 |
428 | +++ deps/layer/layer-apt/.gitignore 2017-02-15 08:51:47 +0000 |
429 | @@ -0,0 +1,2 @@ |
430 | +*~ |
431 | +*.pyc |
432 | |
433 | === added file 'deps/layer/layer-apt/README.md' |
434 | --- deps/layer/layer-apt/README.md 1970-01-01 00:00:00 +0000 |
435 | +++ deps/layer/layer-apt/README.md 2017-02-15 08:51:47 +0000 |
436 | @@ -0,0 +1,195 @@ |
437 | +# Apt layer |
438 | + |
439 | +The Apt layer for Juju enables layered charms to more easily deal with |
440 | +deb packages and apt sources in a simple and efficient manner. It |
441 | +provides consistent configuration for operators, allowing them to |
442 | +easily specify custom apt sources and additional debs required for |
443 | +their particular installations. |
444 | + |
445 | +## Configuration |
446 | + |
447 | +The charm may provide defaults for these service configuration |
448 | +(config.yaml) options, and the operator may override them as required. |
449 | + |
450 | +* `extra_packages` |
451 | + |
452 | + A space separated list of additional deb packages to install on |
453 | + each unit. |
454 | + |
455 | +* `package_status` |
456 | + |
457 | + 'install' or 'hold'. When set to hold, packages installed using |
458 | + the Apt layer API will be pinned, so that they will not be |
459 | + automatically upgraded when package updates are performed. 'hold' |
460 | + is particularly useful for allowing a service such as Landscape |
461 | + to automatically apply security updates to most of the system, |
462 | + whilst holding back any potentially service affecting updates. |
463 | + |
464 | +* `install_sources` |
465 | + |
466 | + A list of apt sources containing the packages that need to be installed. |
467 | + Each source may be either a line that can be added directly to |
468 | + sources.list(5), or in the form ppa:<user>/<ppa-name> for adding |
469 | + Personal Package Archives, or a distribution component to enable. |
470 | + The list is a yaml list, encoded as a string. The nicest way of |
471 | + declaring this in a yaml file looks like the following (in particular, |
472 | + the | character indicates that the value is a multiline string): |
473 | + |
474 | + ```yaml |
475 | + install_sources: | |
476 | + - ppa:stub/cassandra |
477 | + - deb http://www.apache.org/dist/cassandra/debian 21x main |
478 | + ``` |
479 | + |
480 | +* `install_keys` |
481 | + |
482 | + A list of GPG signing keys to accept. There needs to be one entry |
483 | + per entry in install_sources. null may be used if no keep is |
484 | + needed, which is the case for PPAs and for the standard Ubuntu |
485 | + archives. Keys should be full ASCII armoured GPG public keys. |
486 | + GPG key ids are also accepted, but in most environments this |
487 | + mechanism is not secure. The install_keys list, like |
488 | + install_sources, must also be a yaml formatted list encoded as |
489 | + a string: |
490 | + |
491 | + ```yaml |
492 | + install_keys: | |
493 | + - null |
494 | + - | |
495 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
496 | + Version: GnuPG v1 |
497 | + |
498 | + mQINBFQJvgUBEAC0KcYCTj0hd15p4fiXBsbob0sKgsvN5Lm7N9jzJWlGshJ0peMi |
499 | + kH8YhDXw5Lh+mPEHksL7t1L8CIr1a+ntns/Opt65ZPO38ENVkOqEVAn9Z5sIoZsb |
500 | + AUeLlJzSeRLTKhcOugK7UcsQD2FHnMBJz50bxis9X7pjmnc/tWpjAGJfaWdjDIo= |
501 | + =yiQ4 |
502 | + -----END PGP PUBLIC KEY BLOCK----- |
503 | + ``` |
504 | + |
505 | +## Usage |
506 | + |
507 | +Queue packages for installation, and have handlers waiting for |
508 | +these packages to finish being installed: |
509 | + |
510 | +```python |
511 | +import charms.apt |
512 | + |
513 | +@hook('install') |
514 | +def install(): |
515 | + charms.apt.queue_install(['git']) |
516 | + |
517 | +@when_not('apt.installed.gnupg') |
518 | +def install_gnupg(): |
519 | + charms.apt.queue_install(['gnupg']) |
520 | + |
521 | +@when('apt.installed.git') |
522 | +@when('apt.installed.gnupg') |
523 | +def grabit(): |
524 | + clone_repo() |
525 | + validate_repo() |
526 | +``` |
527 | + |
528 | +### API |
529 | + |
530 | +Several methods are exposed in the charms.apt Python package. |
531 | + |
532 | +* `add_source(source, key=None)` |
533 | + |
534 | + Add an apt source. |
535 | + |
536 | + A source may be either a line that can be added directly to |
537 | + sources.list(5), or in the form ppa:<user>/<ppa-name> for adding |
538 | + Personal Package Archives, or a distribution component to enable. |
539 | + |
540 | + The package signing key should be an ASCII armoured GPG key. While |
541 | + GPG key ids are also supported, the retrieval mechanism is insecure. |
542 | + There is no need to specify the package signing key for PPAs or for |
543 | + the main Ubuntu archives. |
544 | + |
545 | + It is preferable if charms do not call this directly to hard |
546 | + coded apt sources, but instead have these sources listed |
547 | + as defaults in the install_sources config option. This allows |
548 | + operators to mirror your packages to internal archives and |
549 | + deploy your charm in environments without network access. |
550 | + |
551 | + Sets the `apt.needs_update` reactive state. |
552 | + |
553 | +* `queue_install(packages, options=None)` |
554 | + |
555 | + Queue one or more deb packages for install. The actual package |
556 | + installation will be performed later by a handler in the |
557 | + apt layer. The `apt.installed.{name}` state will be set once |
558 | + the package installed (one state for each package). |
559 | + |
560 | + If a package has already been installed it will not be reinstalled. |
561 | + |
562 | + If a package has already been queued it will not be requeued, and |
563 | + the install options will not be changed. |
564 | + |
565 | +* `installed()` |
566 | + |
567 | + Returns the set of deb packages installed by this layer. |
568 | + |
569 | +* `purge(packages)` |
570 | + |
571 | + Purge one or more deb packages from the system |
572 | + |
573 | + |
574 | +### Extras |
575 | + |
576 | +These methods are called automatically by the reactive framework as |
577 | +reactive state demands. However, you can also invoke them directly |
578 | +if you want the operation done right now. |
579 | + |
580 | +* `update()` |
581 | + |
582 | + Update the apt cache. Removes the `apt.needs_update` state. |
583 | + |
584 | + |
585 | +* `install_queued()` |
586 | + |
587 | + Installs deb packages queued for installation. On success, removes |
588 | + the `apt.queued_installs` state, sets the `apt.installed.{packagename}` |
589 | + state for each installed package, and returns True. On failure, |
590 | + sets the unit workload status to blocked and returns False. |
591 | + The package installs remain queued. |
592 | + |
593 | + |
594 | +### Layer Options |
595 | + |
596 | +Packages can be specified at charm-build time in `layer.yaml`. List the |
597 | +packages in the 'basic' or 'apt' sections. |
598 | + |
599 | +```yaml |
600 | +includes: |
601 | + - layer:basic |
602 | + - layer:apt |
603 | +options: |
604 | + basic: |
605 | + packages: |
606 | + - python3-psycopg2 |
607 | + apt: |
608 | + packages: |
609 | + - git |
610 | + - bzr |
611 | +``` |
612 | + |
613 | +Packages required to import your Python reactive handlers should go |
614 | +under 'basic'. These get installed by the base layer very early during |
615 | +charm bootstrap, and only packages available in the main Ubuntu archive |
616 | +can go here. Other packages should go under 'apt'. These will be |
617 | +installed later, after custom apt sources such as PPAs have been added |
618 | +from the `install_sources` configuration option. |
619 | + |
620 | + |
621 | +## Support |
622 | + |
623 | +This layer is maintained on Launchpad by |
624 | +Stuart Bishop (stuart.bishop@canonical.com). |
625 | + |
626 | +Code is available using git at git+ssh://git.launchpad.net/layer-apt. |
627 | + |
628 | +Bug reports can be made at https://bugs.launchpad.net/layer-apt. |
629 | + |
630 | +Queries and comments can be made on the Juju mailing list, Juju IRC |
631 | +channels, or at https://answers.launchpad.net/layer-apt. |
632 | |
633 | === added file 'deps/layer/layer-apt/config.yaml' |
634 | --- deps/layer/layer-apt/config.yaml 1970-01-01 00:00:00 +0000 |
635 | +++ deps/layer/layer-apt/config.yaml 2017-02-15 08:51:47 +0000 |
636 | @@ -0,0 +1,34 @@ |
637 | +options: |
638 | + extra_packages: |
639 | + description: > |
640 | + Space separated list of extra deb packages to install. |
641 | + type: string |
642 | + default: "" |
643 | + package_status: |
644 | + default: "install" |
645 | + type: string |
646 | + description: > |
647 | + The status of service-affecting packages will be set to this |
648 | + value in the dpkg database. Valid values are "install" and "hold". |
649 | + install_sources: |
650 | + description: > |
651 | + List of extra apt sources, per charm-helpers standard |
652 | + format (a yaml list of strings encoded as a string). Each source |
653 | + may be either a line that can be added directly to |
654 | + sources.list(5), or in the form ppa:<user>/<ppa-name> for adding |
655 | + Personal Package Archives, or a distribution component to enable. |
656 | + type: string |
657 | + default: "" |
658 | + install_keys: |
659 | + description: > |
660 | + List of signing keys for install_sources package sources, per |
661 | + charmhelpers standard format (a yaml list of strings encoded as |
662 | + a string). The keys should be the full ASCII armoured GPG public |
663 | + keys. While GPG key ids are also supported and looked up on a |
664 | + keyserver, operators should be aware that this mechanism is |
665 | + insecure. null can be used if a standard package signing key is |
666 | + used that will already be installed on the machine, and for PPA |
667 | + sources where the package signing key is securely retrieved from |
668 | + Launchpad. |
669 | + type: string |
670 | + default: "" |
671 | |
672 | === added file 'deps/layer/layer-apt/copyright' |
673 | --- deps/layer/layer-apt/copyright 1970-01-01 00:00:00 +0000 |
674 | +++ deps/layer/layer-apt/copyright 2017-02-15 08:51:47 +0000 |
675 | @@ -0,0 +1,15 @@ |
676 | +Copyright 2015-2016 Canonical Ltd. |
677 | + |
678 | +This file is part of the Apt layer for Juju. |
679 | + |
680 | +This program is free software: you can redistribute it and/or modify |
681 | +it under the terms of the GNU General Public License version 3, as |
682 | +published by the Free Software Foundation. |
683 | + |
684 | +This program is distributed in the hope that it will be useful, but |
685 | +WITHOUT ANY WARRANTY; without even the implied warranties of |
686 | +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
687 | +PURPOSE. See the GNU General Public License for more details. |
688 | + |
689 | +You should have received a copy of the GNU General Public License |
690 | +along with this program. If not, see <http://www.gnu.org/licenses/>. |
691 | |
692 | === added file 'deps/layer/layer-apt/layer.yaml' |
693 | --- deps/layer/layer-apt/layer.yaml 1970-01-01 00:00:00 +0000 |
694 | +++ deps/layer/layer-apt/layer.yaml 2017-02-15 08:51:47 +0000 |
695 | @@ -0,0 +1,5 @@ |
696 | +defines: |
697 | + packages: |
698 | + type: array |
699 | + default: [] |
700 | + description: Additional packages to be installed at time of bootstrap |
701 | |
702 | === added directory 'deps/layer/layer-apt/lib' |
703 | === added directory 'deps/layer/layer-apt/lib/charms' |
704 | === added file 'deps/layer/layer-apt/lib/charms/__init__.py' |
705 | --- deps/layer/layer-apt/lib/charms/__init__.py 1970-01-01 00:00:00 +0000 |
706 | +++ deps/layer/layer-apt/lib/charms/__init__.py 2017-02-15 08:51:47 +0000 |
707 | @@ -0,0 +1,2 @@ |
708 | +from pkgutil import extend_path |
709 | +__path__ = extend_path(__path__, __name__) |
710 | |
711 | === added file 'deps/layer/layer-apt/lib/charms/apt.py' |
712 | --- deps/layer/layer-apt/lib/charms/apt.py 1970-01-01 00:00:00 +0000 |
713 | +++ deps/layer/layer-apt/lib/charms/apt.py 2017-02-15 08:51:47 +0000 |
714 | @@ -0,0 +1,182 @@ |
715 | +# Copyright 2015-2016 Canonical Ltd. |
716 | +# |
717 | +# This file is part of the Apt layer for Juju. |
718 | +# |
719 | +# This program is free software: you can redistribute it and/or modify |
720 | +# it under the terms of the GNU General Public License version 3, as |
721 | +# published by the Free Software Foundation. |
722 | +# |
723 | +# This program is distributed in the hope that it will be useful, but |
724 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
725 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
726 | +# PURPOSE. See the GNU General Public License for more details. |
727 | +# |
728 | +# You should have received a copy of the GNU General Public License |
729 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
730 | + |
731 | +''' |
732 | +charms.reactive helpers for dealing with deb packages. |
733 | + |
734 | +Add apt package sources using add_source(). Queue deb packages for |
735 | +installation with install(). Configure and work with your software |
736 | +once the apt.installed.{packagename} state is set. |
737 | +''' |
738 | +import itertools |
739 | +import subprocess |
740 | + |
741 | +from charmhelpers import fetch |
742 | +from charmhelpers.core import hookenv, unitdata |
743 | +from charms import reactive |
744 | + |
745 | + |
746 | +__all__ = ['add_source', 'update', 'queue_install', 'install_queued', |
747 | + 'installed', 'purge', 'ensure_package_status'] |
748 | + |
749 | + |
750 | +def add_source(source, key=None): |
751 | + '''Add an apt source. |
752 | + |
753 | + Sets the apt.needs_update state. |
754 | + |
755 | + A source may be either a line that can be added directly to |
756 | + sources.list(5), or in the form ppa:<user>/<ppa-name> for adding |
757 | + Personal Package Archives, or a distribution component to enable. |
758 | + |
759 | + The package signing key should be an ASCII armoured GPG key. While |
760 | + GPG key ids are also supported, the retrieval mechanism is insecure. |
761 | + There is no need to specify the package signing key for PPAs or for |
762 | + the main Ubuntu archives. |
763 | + ''' |
764 | + # Maybe we should remember which sources have been added already |
765 | + # so we don't waste time re-adding them. Is this time significant? |
766 | + fetch.add_source(source, key) |
767 | + reactive.set_state('apt.needs_update') |
768 | + |
769 | + |
770 | +def queue_install(packages, options=None): |
771 | + """Queue one or more deb packages for install. |
772 | + |
773 | + The `apt.installed.{name}` state is set once the package is installed. |
774 | + |
775 | + If a package has already been installed it will not be reinstalled. |
776 | + |
777 | + If a package has already been queued it will not be requeued, and |
778 | + the install options will not be changed. |
779 | + |
780 | + Sets the apt.queued_installs state. |
781 | + """ |
782 | + if isinstance(packages, str): |
783 | + packages = [packages] |
784 | + # Filter installed packages. |
785 | + store = unitdata.kv() |
786 | + queued_packages = store.getrange('apt.install_queue.', strip=True) |
787 | + packages = {package: options for package in packages |
788 | + if not (package in queued_packages or |
789 | + reactive.helpers.is_state('apt.installed.' + package))} |
790 | + if packages: |
791 | + unitdata.kv().update(packages, prefix='apt.install_queue.') |
792 | + reactive.set_state('apt.queued_installs') |
793 | + |
794 | + |
795 | +def installed(): |
796 | + '''Return the set of deb packages completed install''' |
797 | + return set(state.split('.', 2)[2] for state in reactive.bus.get_states() |
798 | + if state.startswith('apt.installed.')) |
799 | + |
800 | + |
801 | +def purge(packages): |
802 | + """Purge one or more deb packages from the system""" |
803 | + fetch.apt_purge(packages, fatal=True) |
804 | + store = unitdata.kv() |
805 | + store.unsetrange(packages, prefix='apt.install_queue.') |
806 | + for package in packages: |
807 | + reactive.remove_state('apt.installed.{}'.format(package)) |
808 | + |
809 | + |
810 | +def update(): |
811 | + """Update the apt cache. |
812 | + |
813 | + Removes the apt.needs_update state. |
814 | + """ |
815 | + status_set(None, 'Updating apt cache') |
816 | + fetch.apt_update(fatal=True) # Friends don't let friends set fatal=False |
817 | + reactive.remove_state('apt.needs_update') |
818 | + |
819 | + |
820 | +def install_queued(): |
821 | + '''Installs queued deb packages. |
822 | + |
823 | + Removes the apt.queued_installs state and sets the apt.installed state. |
824 | + |
825 | + On failure, sets the unit's workload state to 'blocked' and returns |
826 | + False. Package installs remain queued. |
827 | + |
828 | + On success, sets the apt.installed.{packagename} state for each |
829 | + installed package and returns True. |
830 | + ''' |
831 | + store = unitdata.kv() |
832 | + queue = sorted((options, package) |
833 | + for package, options in store.getrange('apt.install_queue.', |
834 | + strip=True).items()) |
835 | + |
836 | + installed = set() |
837 | + for options, batch in itertools.groupby(queue, lambda x: x[0]): |
838 | + packages = [b[1] for b in batch] |
839 | + try: |
840 | + status_set(None, 'Installing {}'.format(','.join(packages))) |
841 | + fetch.apt_install(packages, options, fatal=True) |
842 | + store.unsetrange(packages, prefix='apt.install_queue.') |
843 | + installed.update(packages) |
844 | + except subprocess.CalledProcessError: |
845 | + status_set('blocked', |
846 | + 'Unable to install packages {}' |
847 | + .format(','.join(packages))) |
848 | + return False # Without setting reactive state. |
849 | + |
850 | + for package in installed: |
851 | + reactive.set_state('apt.installed.{}'.format(package)) |
852 | + |
853 | + reactive.remove_state('apt.queued_installs') |
854 | + return True |
855 | + |
856 | + |
857 | +def ensure_package_status(): |
858 | + '''Hold or unhold packages per the package_status configuration option. |
859 | + |
860 | + All packages installed using this module and handlers are affected. |
861 | + |
862 | + An mechanism may be added in the future to override this for a |
863 | + subset of installed packages. |
864 | + ''' |
865 | + packages = installed() |
866 | + if not packages: |
867 | + return |
868 | + config = hookenv.config() |
869 | + package_status = config['package_status'] |
870 | + changed = reactive.helpers.data_changed('apt.package_status', |
871 | + (package_status, sorted(packages))) |
872 | + if changed: |
873 | + if package_status == 'hold': |
874 | + hookenv.log('Holding packages {}'.format(','.join(packages))) |
875 | + fetch.apt_hold(packages) |
876 | + else: |
877 | + hookenv.log('Unholding packages {}'.format(','.join(packages))) |
878 | + fetch.apt_unhold(packages) |
879 | + reactive.remove_state('apt.needs_hold') |
880 | + |
881 | + |
882 | +def status_set(state, message): |
883 | + """Set the unit's workload status. |
884 | + |
885 | + Set state == None to keep the same state and just change the message. |
886 | + """ |
887 | + if state is None: |
888 | + state = hookenv.status_get()[0] |
889 | + if state == 'unknown': |
890 | + state = 'maintenance' # Guess |
891 | + if state in ('error', 'blocked'): |
892 | + lvl = hookenv.WARNING |
893 | + else: |
894 | + lvl = hookenv.INFO |
895 | + hookenv.status_set(state, message) |
896 | + hookenv.log('{}: {}'.format(state, message), lvl) |
897 | |
898 | === added directory 'deps/layer/layer-apt/reactive' |
899 | === added file 'deps/layer/layer-apt/reactive/apt.py' |
900 | --- deps/layer/layer-apt/reactive/apt.py 1970-01-01 00:00:00 +0000 |
901 | +++ deps/layer/layer-apt/reactive/apt.py 2017-02-15 08:51:47 +0000 |
902 | @@ -0,0 +1,131 @@ |
903 | +# Copyright 2015-2016 Canonical Ltd. |
904 | +# |
905 | +# This file is part of the Apt layer for Juju. |
906 | +# |
907 | +# This program is free software: you can redistribute it and/or modify |
908 | +# it under the terms of the GNU General Public License version 3, as |
909 | +# published by the Free Software Foundation. |
910 | +# |
911 | +# This program is distributed in the hope that it will be useful, but |
912 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
913 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
914 | +# PURPOSE. See the GNU General Public License for more details. |
915 | +# |
916 | +# You should have received a copy of the GNU General Public License |
917 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
918 | + |
919 | +''' |
920 | +charms.reactive helpers for dealing with deb packages. |
921 | + |
922 | +Add apt package sources using add_source(). Queue deb packages for |
923 | +installation with install(). Configure and work with your software |
924 | +once the apt.installed.{packagename} state is set. |
925 | +''' |
926 | +import subprocess |
927 | + |
928 | +from charmhelpers import fetch |
929 | +from charmhelpers.core import hookenv |
930 | +from charmhelpers.core.hookenv import WARNING |
931 | +from charms import layer |
932 | +from charms import reactive |
933 | +from charms.reactive import when, when_not |
934 | + |
935 | +import charms.apt |
936 | + |
937 | + |
938 | +@when('apt.needs_update') |
939 | +def update(): |
940 | + charms.apt.update() |
941 | + |
942 | + |
943 | +@when('apt.queued_installs') |
944 | +@when_not('apt.needs_update') |
945 | +def install_queued(): |
946 | + charms.apt.install_queued() |
947 | + |
948 | + |
949 | +@when_not('apt.queued_installs') |
950 | +def ensure_package_status(): |
951 | + charms.apt.ensure_package_status() |
952 | + |
953 | + |
954 | +def filter_installed_packages(packages): |
955 | + # Don't use fetch.filter_installed_packages, as it depends on python-apt |
956 | + # and not available if the basic layer's use_site_packages option is off |
957 | + # TODO: Move this to charm-helpers.fetch |
958 | + cmd = ['dpkg-query', '--show', r'--showformat=${Package}\n'] |
959 | + installed = set(subprocess.check_output(cmd, |
960 | + universal_newlines=True).split()) |
961 | + return set(packages) - installed |
962 | + |
963 | + |
964 | +def clear_removed_package_states(): |
965 | + """On hook startup, clear install states for removed packages.""" |
966 | + removed = filter_installed_packages(charms.apt.installed()) |
967 | + if removed: |
968 | + hookenv.log('{} missing packages ({})'.format(len(removed), |
969 | + ','.join(removed)), |
970 | + WARNING) |
971 | + for package in removed: |
972 | + reactive.remove_state('apt.installed.{}'.format(package)) |
973 | + |
974 | + |
975 | +def configure_sources(): |
976 | + """Add user specified package sources from the service configuration. |
977 | + |
978 | + See charmhelpers.fetch.configure_sources for details. |
979 | + """ |
980 | + config = hookenv.config() |
981 | + |
982 | + # We don't have enums, so we need to validate this ourselves. |
983 | + package_status = config.get('package_status') |
984 | + if package_status not in ('hold', 'install'): |
985 | + charms.apt.status_set('blocked', |
986 | + 'Unknown package_status {}' |
987 | + ''.format(package_status)) |
988 | + # Die before further hooks are run. This isn't very nice, but |
989 | + # there is no other way to inform the operator that they have |
990 | + # invalid configuration. |
991 | + raise SystemExit(0) |
992 | + |
993 | + sources = config.get('install_sources') |
994 | + keys = config.get('install_keys') |
995 | + if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)): |
996 | + fetch.configure_sources(update=False, |
997 | + sources_var='install_sources', |
998 | + keys_var='install_keys') |
999 | + reactive.set_state('apt.needs_update') |
1000 | + |
1001 | + extra_packages = sorted(config.get('extra_packages', '').split()) |
1002 | + if extra_packages: |
1003 | + charms.apt.queue_install(extra_packages) |
1004 | + |
1005 | + |
1006 | +def queue_layer_packages(): |
1007 | + """Add packages listed in build-time layer options.""" |
1008 | + # Both basic and apt layer. basic layer will have already installed |
1009 | + # its defined packages, but rescheduling it here gets the apt layer |
1010 | + # state set and they will pinned as any other apt layer installed |
1011 | + # package. |
1012 | + opts = layer.options() |
1013 | + for section in ['basic', 'apt']: |
1014 | + if section in opts and 'packages' in opts[section]: |
1015 | + charms.apt.queue_install(opts[section]['packages']) |
1016 | + |
1017 | + |
1018 | +# Per https://github.com/juju-solutions/charms.reactive/issues/33, |
1019 | +# this module may be imported multiple times so ensure the |
1020 | +# initialization hook is only registered once. I have to piggy back |
1021 | +# onto the namespace of a module imported before reactive discovery |
1022 | +# to do this. |
1023 | +if not hasattr(reactive, '_apt_registered'): |
1024 | + # We need to register this to run every hook, not just during install |
1025 | + # and config-changed, to protect against race conditions. If we don't |
1026 | + # do this, then the config in the hook environment may show updates |
1027 | + # to running hooks well before the config-changed hook has been invoked |
1028 | + # and the intialization provided an opertunity to be run. |
1029 | + hookenv.atstart(hookenv.log, 'Initializing Apt Layer') |
1030 | + hookenv.atstart(clear_removed_package_states) |
1031 | + hookenv.atstart(configure_sources) |
1032 | + hookenv.atstart(queue_layer_packages) |
1033 | + reactive._apt_registered = True |
1034 | |
1035 | === added directory 'deps/layer/layer-basic' |
1036 | === added file 'deps/layer/layer-basic/.gitignore' |
1037 | --- deps/layer/layer-basic/.gitignore 1970-01-01 00:00:00 +0000 |
1038 | +++ deps/layer/layer-basic/.gitignore 2017-02-15 08:51:47 +0000 |
1039 | @@ -0,0 +1,5 @@ |
1040 | +*.pyc |
1041 | +*~ |
1042 | +.ropeproject |
1043 | +.settings |
1044 | +.tox |
1045 | |
1046 | === added file 'deps/layer/layer-basic/Makefile' |
1047 | --- deps/layer/layer-basic/Makefile 1970-01-01 00:00:00 +0000 |
1048 | +++ deps/layer/layer-basic/Makefile 2017-02-15 08:51:47 +0000 |
1049 | @@ -0,0 +1,24 @@ |
1050 | +#!/usr/bin/make |
1051 | + |
1052 | +all: lint unit_test |
1053 | + |
1054 | + |
1055 | +.PHONY: clean |
1056 | +clean: |
1057 | + @rm -rf .tox |
1058 | + |
1059 | +.PHONY: apt_prereqs |
1060 | +apt_prereqs: |
1061 | + @# Need tox, but don't install the apt version unless we have to (don't want to conflict with pip) |
1062 | + @which tox >/dev/null || (sudo apt-get install -y python-pip && sudo pip install tox) |
1063 | + |
1064 | +.PHONY: lint |
1065 | +lint: apt_prereqs |
1066 | + @tox --notest |
1067 | + @PATH=.tox/py34/bin:.tox/py35/bin flake8 $(wildcard hooks reactive lib unit_tests tests) |
1068 | + @charm proof |
1069 | + |
1070 | +.PHONY: unit_test |
1071 | +unit_test: apt_prereqs |
1072 | + @echo Starting tests... |
1073 | + tox |
1074 | |
1075 | === added file 'deps/layer/layer-basic/README.md' |
1076 | --- deps/layer/layer-basic/README.md 1970-01-01 00:00:00 +0000 |
1077 | +++ deps/layer/layer-basic/README.md 2017-02-15 08:51:47 +0000 |
1078 | @@ -0,0 +1,221 @@ |
1079 | +# Overview |
1080 | + |
1081 | +This is the base layer for all charms [built using layers][building]. It |
1082 | +provides all of the standard Juju hooks and runs the |
1083 | +[charms.reactive.main][charms.reactive] loop for them. It also bootstraps the |
1084 | +[charm-helpers][] and [charms.reactive][] libraries and all of their |
1085 | +dependencies for use by the charm. |
1086 | + |
1087 | +# Usage |
1088 | + |
1089 | +To create a charm layer using this base layer, you need only include it in |
1090 | +a `layer.yaml` file: |
1091 | + |
1092 | +```yaml |
1093 | +includes: ['layer:basic'] |
1094 | +``` |
1095 | + |
1096 | +This will fetch this layer from [interfaces.juju.solutions][] and incorporate |
1097 | +it into your charm layer. You can then add handlers under the `reactive/` |
1098 | +directory. Note that **any** file under `reactive/` will be expected to |
1099 | +contain handlers, whether as Python decorated functions or [executables][non-python] |
1100 | +using the [external handler protocol][]. |
1101 | + |
1102 | +### Charm Dependencies |
1103 | + |
1104 | +Each layer can include a `wheelhouse.txt` file with Python requirement lines. |
1105 | +For example, this layer's `wheelhouse.txt` includes: |
1106 | + |
1107 | +``` |
1108 | +pip>=7.0.0,<8.0.0 |
1109 | +charmhelpers>=0.4.0,<1.0.0 |
1110 | +charms.reactive>=0.1.0,<2.0.0 |
1111 | +``` |
1112 | + |
1113 | +All of these dependencies from each layer will be fetched (and updated) at build |
1114 | +time and will be automatically installed by this base layer before any reactive |
1115 | +handlers are run. |
1116 | + |
1117 | +Note that the `wheelhouse.txt` file is intended for **charm** dependencies only. |
1118 | +That is, for libraries that the charm code itself needs to do its job of deploying |
1119 | +and configuring the payload. If the payload itself has Python dependencies, those |
1120 | +should be handled separately, by the charm. |
1121 | + |
1122 | +See [PyPI][pypi charms.X] for packages under the `charms.` namespace which might |
1123 | +be useful for your charm. |
1124 | + |
1125 | +### Layer Namespace |
1126 | + |
1127 | +Each layer has a reserved section in the `charms.layer.` Python package namespace, |
1128 | +which it can populate by including a `lib/charms/layer/<layer-name>.py` file or |
1129 | +by placing files under `lib/charms/layer/<layer-name>/`. (If the layer name |
1130 | +includes hyphens, replace them with underscores.) These can be helpers that the |
1131 | +layer uses internally, or it can expose classes or functions to be used by other |
1132 | +layers to interact with that layer. |
1133 | + |
1134 | +For example, a layer named `foo` could include a `lib/charms/layer/foo.py` file |
1135 | +with some helper functions that other layers could access using: |
1136 | + |
1137 | +```python |
1138 | +from charms.layer.foo import my_helper |
1139 | +``` |
1140 | + |
1141 | +### Layer Options |
1142 | + |
1143 | +Any layer can define options in its `layer.yaml`. Those options can then be set |
1144 | +by other layers to change the behavior of your layer. The options are defined |
1145 | +using [jsonschema][], which is the same way that [action paramters][] are defined. |
1146 | + |
1147 | +For example, the `foo` layer could include the following option definitons: |
1148 | + |
1149 | +```yaml |
1150 | +includes: ['layer:basic'] |
1151 | +defines: # define some options for this layer (the layer "foo") |
1152 | + enable-bar: # define an "enable-bar" option for this layer |
1153 | + description: If true, enable support for "bar". |
1154 | + type: boolean |
1155 | + default: false |
1156 | +``` |
1157 | + |
1158 | +A layer using `foo` could then set it: |
1159 | + |
1160 | +```yaml |
1161 | +includes: ['layer:foo'] |
1162 | +options: |
1163 | + foo: # setting options for the "foo" layer |
1164 | + enable-bar: true # set the "enable-bar" option to true |
1165 | +``` |
1166 | + |
1167 | +The `foo` layer can then use the `charms.layer.options` helper to load the values |
1168 | +for the options that it defined. For example: |
1169 | + |
1170 | +```python |
1171 | +from charms import layer |
1172 | + |
1173 | +@when('state') |
1174 | +def do_thing(): |
1175 | + layer_opts = layer.options('foo') # load all of the options for the "foo" layer |
1176 | + if layer_opts['enable-bar']: # check the value of the "enable-bar" option |
1177 | + hookenv.log("Bar is enabled") |
1178 | +``` |
1179 | + |
1180 | +You can also access layer options in other handlers, such as Bash, using |
1181 | +the command-line interface: |
1182 | + |
1183 | +```bash |
1184 | +. charms.reactive.sh |
1185 | + |
1186 | +@when 'state' |
1187 | +function do_thing() { |
1188 | + if layer_option foo enable-bar; then |
1189 | + juju-log "Bar is enabled" |
1190 | + juju-log "bar-value is: $(layer_option foo bar-value)" |
1191 | + fi |
1192 | +} |
1193 | + |
1194 | +reactive_handler_main |
1195 | +``` |
1196 | + |
1197 | +Note that options of type `boolean` will set the exit code, while other types |
1198 | +will be printed out. |
1199 | + |
1200 | +# Hooks |
1201 | + |
1202 | +This layer provides hooks that other layers can react to using the decorators |
1203 | +of the [charms.reactive][] library: |
1204 | + |
1205 | + * `config-changed` |
1206 | + * `install` |
1207 | + * `leader-elected` |
1208 | + * `leader-settings-changed` |
1209 | + * `start` |
1210 | + * `stop` |
1211 | + * `upgrade-charm` |
1212 | + * `update-status` |
1213 | + |
1214 | +Other hooks are not implemented at this time. A new layer can implement storage |
1215 | +or relation hooks in their own layer by putting them in the `hooks` directory. |
1216 | + |
1217 | +**Note:** Because `update-status` is invoked every 5 minutes, you should take |
1218 | +care to ensure that your reactive handlers only invoke expensive operations |
1219 | +when absolutely necessary. It is recommended that you use helpers like |
1220 | +[`@only_once`][], [`@when_file_changed`][], and [`data_changed`][] to ensure |
1221 | +that handlers run only when necessary. |
1222 | + |
1223 | +# Layer Configuration |
1224 | + |
1225 | +This layer supports the following options, which can be set in `layer.yaml`: |
1226 | + |
1227 | + * **packages** A list of system packages to be installed before the reactive |
1228 | + handlers are invoked. |
1229 | + |
1230 | + * **use_venv** If set to true, the charm dependencies from the various |
1231 | + layers' `wheelhouse.txt` files will be installed in a Python virtualenv |
1232 | + located at `$CHARM_DIR/../.venv`. This keeps charm dependencies from |
1233 | + conflicting with payload dependencies, but you must take care to preserve |
1234 | + the environment and interpreter if using `execl` or `subprocess`. |
1235 | + |
1236 | + * **include_system_packages** If set to true and using a venv, include |
1237 | + the `--system-site-packages` options to make system Python libraries |
1238 | + visible within the venv. |
1239 | + |
1240 | +An example `layer.yaml` using these options might be: |
1241 | + |
1242 | +```yaml |
1243 | +includes: ['layer:basic'] |
1244 | +options: |
1245 | + basic: |
1246 | + packages: ['git'] |
1247 | + use_venv: true |
1248 | + include_system_packages: true |
1249 | +``` |
1250 | + |
1251 | + |
1252 | +# Reactive States |
1253 | + |
1254 | +This layer will set the following states: |
1255 | + |
1256 | + * **`config.changed`** Any config option has changed from its previous value. |
1257 | + This state is cleared automatically at the end of each hook invocation. |
1258 | + |
1259 | + * **`config.changed.<option>`** A specific config option has changed. |
1260 | + **`<option>`** will be replaced by the config option name from `config.yaml`. |
1261 | + This state is cleared automatically at the end of each hook invocation. |
1262 | + |
1263 | + * **`config.set.<option>`** A specific config option has a True or non-empty |
1264 | + value set. **`<option>`** will be replaced by the config option name from |
1265 | + `config.yaml`. This state is cleared automatically at the end of each hook |
1266 | + invocation. |
1267 | + |
1268 | + * **`config.default.<option>`** A specific config option is set to its |
1269 | + default value. **`<option>`** will be replaced by the config option name |
1270 | + from `config.yaml`. This state is cleared automatically at the end of |
1271 | + each hook invocation. |
1272 | + |
1273 | +An example using the config states would be: |
1274 | + |
1275 | +```python |
1276 | +@when('config.changed.my-opt') |
1277 | +def my_opt_changed(): |
1278 | + update_config() |
1279 | + restart_service() |
1280 | +``` |
1281 | + |
1282 | + |
1283 | +# Actions |
1284 | + |
1285 | +This layer currently does not define any actions. |
1286 | + |
1287 | + |
1288 | +[building]: https://jujucharms.com/docs/devel/authors-charm-building |
1289 | +[charm-helpers]: https://pythonhosted.org/charmhelpers/ |
1290 | +[charms.reactive]: https://pythonhosted.org/charms.reactive/ |
1291 | +[interfaces.juju.solutions]: http://interfaces.juju.solutions/ |
1292 | +[non-python]: https://pythonhosted.org/charms.reactive/#non-python-reactive-handlers |
1293 | +[external handler protocol]: https://pythonhosted.org/charms.reactive/charms.reactive.bus.html#charms.reactive.bus.ExternalHandler |
1294 | +[jsonschema]: http://json-schema.org/ |
1295 | +[action paramters]: https://jujucharms.com/docs/stable/authors-charm-actions |
1296 | +[pypi charms.X]: https://pypi.python.org/pypi?%3Aaction=search&term=charms.&submit=search |
1297 | +[`@only_once`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.only_once |
1298 | +[`@when_file_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.decorators.html#charms.reactive.decorators.when_file_changed |
1299 | +[`data_changed`]: https://pythonhosted.org/charms.reactive/charms.reactive.helpers.html#charms.reactive.helpers.data_changed |
1300 | |
1301 | === added directory 'deps/layer/layer-basic/bin' |
1302 | === added file 'deps/layer/layer-basic/bin/layer_option' |
1303 | --- deps/layer/layer-basic/bin/layer_option 1970-01-01 00:00:00 +0000 |
1304 | +++ deps/layer/layer-basic/bin/layer_option 2017-02-15 08:51:47 +0000 |
1305 | @@ -0,0 +1,24 @@ |
1306 | +#!/usr/bin/env python3 |
1307 | + |
1308 | +import sys |
1309 | +sys.path.append('lib') |
1310 | + |
1311 | +import argparse |
1312 | +from charms.layer import options |
1313 | + |
1314 | + |
1315 | +parser = argparse.ArgumentParser(description='Access layer options.') |
1316 | +parser.add_argument('section', |
1317 | + help='the section, or layer, the option is from') |
1318 | +parser.add_argument('option', |
1319 | + help='the option to access') |
1320 | + |
1321 | +args = parser.parse_args() |
1322 | +value = options(args.section).get(args.option, '') |
1323 | +if isinstance(value, bool): |
1324 | + sys.exit(0 if value else 1) |
1325 | +elif isinstance(value, list): |
1326 | + for val in value: |
1327 | + print(val) |
1328 | +else: |
1329 | + print(value) |
1330 | |
1331 | === added file 'deps/layer/layer-basic/copyright' |
1332 | --- deps/layer/layer-basic/copyright 1970-01-01 00:00:00 +0000 |
1333 | +++ deps/layer/layer-basic/copyright 2017-02-15 08:51:47 +0000 |
1334 | @@ -0,0 +1,9 @@ |
1335 | +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 |
1336 | + |
1337 | +Files: * |
1338 | +Copyright: 2015, Canonical Ltd. |
1339 | +License: GPL-3 |
1340 | + |
1341 | +License: GPL-3 |
1342 | + On Debian GNU/Linux system you can find the complete text of the |
1343 | + GPL-3 license in '/usr/share/common-licenses/GPL-3' |
1344 | |
1345 | === added directory 'deps/layer/layer-basic/hooks' |
1346 | === added file 'deps/layer/layer-basic/hooks/config-changed' |
1347 | --- deps/layer/layer-basic/hooks/config-changed 1970-01-01 00:00:00 +0000 |
1348 | +++ deps/layer/layer-basic/hooks/config-changed 2017-02-15 08:51:47 +0000 |
1349 | @@ -0,0 +1,19 @@ |
1350 | +#!/usr/bin/env python3 |
1351 | + |
1352 | +# Load modules from $CHARM_DIR/lib |
1353 | +import sys |
1354 | +sys.path.append('lib') |
1355 | + |
1356 | +from charms.layer import basic |
1357 | +basic.bootstrap_charm_deps() |
1358 | +basic.init_config_states() |
1359 | + |
1360 | + |
1361 | +# This will load and run the appropriate @hook and other decorated |
1362 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1363 | +# and $CHARM_DIR/hooks/relations. |
1364 | +# |
1365 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1366 | +# for more information on this pattern. |
1367 | +from charms.reactive import main |
1368 | +main() |
1369 | |
1370 | === added file 'deps/layer/layer-basic/hooks/hook.template' |
1371 | --- deps/layer/layer-basic/hooks/hook.template 1970-01-01 00:00:00 +0000 |
1372 | +++ deps/layer/layer-basic/hooks/hook.template 2017-02-15 08:51:47 +0000 |
1373 | @@ -0,0 +1,19 @@ |
1374 | +#!/usr/bin/env python3 |
1375 | + |
1376 | +# Load modules from $CHARM_DIR/lib |
1377 | +import sys |
1378 | +sys.path.append('lib') |
1379 | + |
1380 | +from charms.layer import basic |
1381 | +basic.bootstrap_charm_deps() |
1382 | +basic.init_config_states() |
1383 | + |
1384 | + |
1385 | +# This will load and run the appropriate @hook and other decorated |
1386 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1387 | +# and $CHARM_DIR/hooks/relations. |
1388 | +# |
1389 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1390 | +# for more information on this pattern. |
1391 | +from charms.reactive import main |
1392 | +main() |
1393 | |
1394 | === added file 'deps/layer/layer-basic/hooks/install' |
1395 | --- deps/layer/layer-basic/hooks/install 1970-01-01 00:00:00 +0000 |
1396 | +++ deps/layer/layer-basic/hooks/install 2017-02-15 08:51:47 +0000 |
1397 | @@ -0,0 +1,19 @@ |
1398 | +#!/usr/bin/env python3 |
1399 | + |
1400 | +# Load modules from $CHARM_DIR/lib |
1401 | +import sys |
1402 | +sys.path.append('lib') |
1403 | + |
1404 | +from charms.layer import basic |
1405 | +basic.bootstrap_charm_deps() |
1406 | +basic.init_config_states() |
1407 | + |
1408 | + |
1409 | +# This will load and run the appropriate @hook and other decorated |
1410 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1411 | +# and $CHARM_DIR/hooks/relations. |
1412 | +# |
1413 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1414 | +# for more information on this pattern. |
1415 | +from charms.reactive import main |
1416 | +main() |
1417 | |
1418 | === added file 'deps/layer/layer-basic/hooks/leader-elected' |
1419 | --- deps/layer/layer-basic/hooks/leader-elected 1970-01-01 00:00:00 +0000 |
1420 | +++ deps/layer/layer-basic/hooks/leader-elected 2017-02-15 08:51:47 +0000 |
1421 | @@ -0,0 +1,19 @@ |
1422 | +#!/usr/bin/env python3 |
1423 | + |
1424 | +# Load modules from $CHARM_DIR/lib |
1425 | +import sys |
1426 | +sys.path.append('lib') |
1427 | + |
1428 | +from charms.layer import basic |
1429 | +basic.bootstrap_charm_deps() |
1430 | +basic.init_config_states() |
1431 | + |
1432 | + |
1433 | +# This will load and run the appropriate @hook and other decorated |
1434 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1435 | +# and $CHARM_DIR/hooks/relations. |
1436 | +# |
1437 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1438 | +# for more information on this pattern. |
1439 | +from charms.reactive import main |
1440 | +main() |
1441 | |
1442 | === added file 'deps/layer/layer-basic/hooks/leader-settings-changed' |
1443 | --- deps/layer/layer-basic/hooks/leader-settings-changed 1970-01-01 00:00:00 +0000 |
1444 | +++ deps/layer/layer-basic/hooks/leader-settings-changed 2017-02-15 08:51:47 +0000 |
1445 | @@ -0,0 +1,19 @@ |
1446 | +#!/usr/bin/env python3 |
1447 | + |
1448 | +# Load modules from $CHARM_DIR/lib |
1449 | +import sys |
1450 | +sys.path.append('lib') |
1451 | + |
1452 | +from charms.layer import basic |
1453 | +basic.bootstrap_charm_deps() |
1454 | +basic.init_config_states() |
1455 | + |
1456 | + |
1457 | +# This will load and run the appropriate @hook and other decorated |
1458 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1459 | +# and $CHARM_DIR/hooks/relations. |
1460 | +# |
1461 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1462 | +# for more information on this pattern. |
1463 | +from charms.reactive import main |
1464 | +main() |
1465 | |
1466 | === added file 'deps/layer/layer-basic/hooks/start' |
1467 | --- deps/layer/layer-basic/hooks/start 1970-01-01 00:00:00 +0000 |
1468 | +++ deps/layer/layer-basic/hooks/start 2017-02-15 08:51:47 +0000 |
1469 | @@ -0,0 +1,19 @@ |
1470 | +#!/usr/bin/env python3 |
1471 | + |
1472 | +# Load modules from $CHARM_DIR/lib |
1473 | +import sys |
1474 | +sys.path.append('lib') |
1475 | + |
1476 | +from charms.layer import basic |
1477 | +basic.bootstrap_charm_deps() |
1478 | +basic.init_config_states() |
1479 | + |
1480 | + |
1481 | +# This will load and run the appropriate @hook and other decorated |
1482 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1483 | +# and $CHARM_DIR/hooks/relations. |
1484 | +# |
1485 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1486 | +# for more information on this pattern. |
1487 | +from charms.reactive import main |
1488 | +main() |
1489 | |
1490 | === added file 'deps/layer/layer-basic/hooks/stop' |
1491 | --- deps/layer/layer-basic/hooks/stop 1970-01-01 00:00:00 +0000 |
1492 | +++ deps/layer/layer-basic/hooks/stop 2017-02-15 08:51:47 +0000 |
1493 | @@ -0,0 +1,19 @@ |
1494 | +#!/usr/bin/env python3 |
1495 | + |
1496 | +# Load modules from $CHARM_DIR/lib |
1497 | +import sys |
1498 | +sys.path.append('lib') |
1499 | + |
1500 | +from charms.layer import basic |
1501 | +basic.bootstrap_charm_deps() |
1502 | +basic.init_config_states() |
1503 | + |
1504 | + |
1505 | +# This will load and run the appropriate @hook and other decorated |
1506 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1507 | +# and $CHARM_DIR/hooks/relations. |
1508 | +# |
1509 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1510 | +# for more information on this pattern. |
1511 | +from charms.reactive import main |
1512 | +main() |
1513 | |
1514 | === added file 'deps/layer/layer-basic/hooks/update-status' |
1515 | --- deps/layer/layer-basic/hooks/update-status 1970-01-01 00:00:00 +0000 |
1516 | +++ deps/layer/layer-basic/hooks/update-status 2017-02-15 08:51:47 +0000 |
1517 | @@ -0,0 +1,19 @@ |
1518 | +#!/usr/bin/env python3 |
1519 | + |
1520 | +# Load modules from $CHARM_DIR/lib |
1521 | +import sys |
1522 | +sys.path.append('lib') |
1523 | + |
1524 | +from charms.layer import basic |
1525 | +basic.bootstrap_charm_deps() |
1526 | +basic.init_config_states() |
1527 | + |
1528 | + |
1529 | +# This will load and run the appropriate @hook and other decorated |
1530 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1531 | +# and $CHARM_DIR/hooks/relations. |
1532 | +# |
1533 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1534 | +# for more information on this pattern. |
1535 | +from charms.reactive import main |
1536 | +main() |
1537 | |
1538 | === added file 'deps/layer/layer-basic/hooks/upgrade-charm' |
1539 | --- deps/layer/layer-basic/hooks/upgrade-charm 1970-01-01 00:00:00 +0000 |
1540 | +++ deps/layer/layer-basic/hooks/upgrade-charm 2017-02-15 08:51:47 +0000 |
1541 | @@ -0,0 +1,28 @@ |
1542 | +#!/usr/bin/env python3 |
1543 | + |
1544 | +# Load modules from $CHARM_DIR/lib |
1545 | +import os |
1546 | +import sys |
1547 | +sys.path.append('lib') |
1548 | + |
1549 | +# This is an upgrade-charm context, make sure we install latest deps |
1550 | +if not os.path.exists('wheelhouse/.upgrade'): |
1551 | + open('wheelhouse/.upgrade', 'w').close() |
1552 | + if os.path.exists('wheelhouse/.bootstrapped'): |
1553 | + os.unlink('wheelhouse/.bootstrapped') |
1554 | +else: |
1555 | + os.unlink('wheelhouse/.upgrade') |
1556 | + |
1557 | +from charms.layer import basic |
1558 | +basic.bootstrap_charm_deps() |
1559 | +basic.init_config_states() |
1560 | + |
1561 | + |
1562 | +# This will load and run the appropriate @hook and other decorated |
1563 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
1564 | +# and $CHARM_DIR/hooks/relations. |
1565 | +# |
1566 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
1567 | +# for more information on this pattern. |
1568 | +from charms.reactive import main |
1569 | +main() |
1570 | |
1571 | === added file 'deps/layer/layer-basic/layer.yaml' |
1572 | --- deps/layer/layer-basic/layer.yaml 1970-01-01 00:00:00 +0000 |
1573 | +++ deps/layer/layer-basic/layer.yaml 2017-02-15 08:51:47 +0000 |
1574 | @@ -0,0 +1,18 @@ |
1575 | +defines: |
1576 | + packages: |
1577 | + type: array |
1578 | + default: [] |
1579 | + description: Additional packages to be installed at time of bootstrap |
1580 | + use_venv: |
1581 | + type: boolean |
1582 | + default: false |
1583 | + description: > |
1584 | + Install charm dependencies (wheelhouse) into a Python virtual environment |
1585 | + to help avoid conflicts with other charms or libraries on the machine. |
1586 | + include_system_packages: |
1587 | + type: boolean |
1588 | + default: false |
1589 | + description: > |
1590 | + If using a virtual environment, allow the venv to see Python packages |
1591 | + installed at the system level. This reduces isolation, but is necessary |
1592 | + to use Python packages installed via apt-get. |
1593 | |
1594 | === added directory 'deps/layer/layer-basic/lib' |
1595 | === added directory 'deps/layer/layer-basic/lib/charms' |
1596 | === added directory 'deps/layer/layer-basic/lib/charms/layer' |
1597 | === added file 'deps/layer/layer-basic/lib/charms/layer/__init__.py' |
1598 | --- deps/layer/layer-basic/lib/charms/layer/__init__.py 1970-01-01 00:00:00 +0000 |
1599 | +++ deps/layer/layer-basic/lib/charms/layer/__init__.py 2017-02-15 08:51:47 +0000 |
1600 | @@ -0,0 +1,21 @@ |
1601 | +import os |
1602 | + |
1603 | + |
1604 | +class LayerOptions(dict): |
1605 | + def __init__(self, layer_file, section=None): |
1606 | + import yaml # defer, might not be available until bootstrap |
1607 | + with open(layer_file) as f: |
1608 | + layer = yaml.safe_load(f.read()) |
1609 | + opts = layer.get('options', {}) |
1610 | + if section and section in opts: |
1611 | + super(LayerOptions, self).__init__(opts.get(section)) |
1612 | + else: |
1613 | + super(LayerOptions, self).__init__(opts) |
1614 | + |
1615 | + |
1616 | +def options(section=None, layer_file=None): |
1617 | + if not layer_file: |
1618 | + base_dir = os.environ.get('CHARM_DIR', os.getcwd()) |
1619 | + layer_file = os.path.join(base_dir, 'layer.yaml') |
1620 | + |
1621 | + return LayerOptions(layer_file, section) |
1622 | |
1623 | === added file 'deps/layer/layer-basic/lib/charms/layer/basic.py' |
1624 | --- deps/layer/layer-basic/lib/charms/layer/basic.py 1970-01-01 00:00:00 +0000 |
1625 | +++ deps/layer/layer-basic/lib/charms/layer/basic.py 2017-02-15 08:51:47 +0000 |
1626 | @@ -0,0 +1,159 @@ |
1627 | +import os |
1628 | +import sys |
1629 | +import shutil |
1630 | +import platform |
1631 | +from glob import glob |
1632 | +from subprocess import check_call |
1633 | + |
1634 | +from charms.layer.execd import execd_preinstall |
1635 | + |
1636 | + |
1637 | +def bootstrap_charm_deps(): |
1638 | + """ |
1639 | + Set up the base charm dependencies so that the reactive system can run. |
1640 | + """ |
1641 | + # execd must happen first, before any attempt to install packages or |
1642 | + # access the network, because sites use this hook to do bespoke |
1643 | + # configuration and install secrets so the rest of this bootstrap |
1644 | + # and the charm itself can actually succeed. This call does nothing |
1645 | + # unless the operator has created and populated $CHARM_DIR/exec.d. |
1646 | + execd_preinstall() |
1647 | + # ensure that $CHARM_DIR/bin is on the path, for helper scripts |
1648 | + os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin') |
1649 | + venv = os.path.abspath('../.venv') |
1650 | + vbin = os.path.join(venv, 'bin') |
1651 | + vpip = os.path.join(vbin, 'pip') |
1652 | + vpy = os.path.join(vbin, 'python') |
1653 | + if os.path.exists('wheelhouse/.bootstrapped'): |
1654 | + from charms import layer |
1655 | + cfg = layer.options('basic') |
1656 | + if cfg.get('use_venv') and '.venv' not in sys.executable: |
1657 | + # activate the venv |
1658 | + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) |
1659 | + reload_interpreter(vpy) |
1660 | + return |
1661 | + # bootstrap wheelhouse |
1662 | + if os.path.exists('wheelhouse'): |
1663 | + with open('/root/.pydistutils.cfg', 'w') as fp: |
1664 | + # make sure that easy_install also only uses the wheelhouse |
1665 | + # (see https://github.com/pypa/pip/issues/410) |
1666 | + charm_dir = os.environ['CHARM_DIR'] |
1667 | + fp.writelines([ |
1668 | + "[easy_install]\n", |
1669 | + "allow_hosts = ''\n", |
1670 | + "find_links = file://{}/wheelhouse/\n".format(charm_dir), |
1671 | + ]) |
1672 | + apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml']) |
1673 | + from charms import layer |
1674 | + cfg = layer.options('basic') |
1675 | + # include packages defined in layer.yaml |
1676 | + apt_install(cfg.get('packages', [])) |
1677 | + # if we're using a venv, set it up |
1678 | + if cfg.get('use_venv'): |
1679 | + if not os.path.exists(venv): |
1680 | + distname, version, series = platform.linux_distribution() |
1681 | + if series in ('precise', 'trusty'): |
1682 | + apt_install(['python-virtualenv']) |
1683 | + else: |
1684 | + apt_install(['virtualenv']) |
1685 | + cmd = ['virtualenv', '-ppython3', '--never-download', venv] |
1686 | + if cfg.get('include_system_packages'): |
1687 | + cmd.append('--system-site-packages') |
1688 | + check_call(cmd) |
1689 | + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) |
1690 | + pip = vpip |
1691 | + else: |
1692 | + pip = 'pip3' |
1693 | + # save a copy of system pip to prevent `pip3 install -U pip` |
1694 | + # from changing it |
1695 | + if os.path.exists('/usr/bin/pip'): |
1696 | + shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') |
1697 | + # need newer pip, to fix spurious Double Requirement error: |
1698 | + # https://github.com/pypa/pip/issues/56 |
1699 | + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', |
1700 | + 'pip']) |
1701 | + # install the rest of the wheelhouse deps |
1702 | + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + |
1703 | + glob('wheelhouse/*')) |
1704 | + if not cfg.get('use_venv'): |
1705 | + # restore system pip to prevent `pip3 install -U pip` |
1706 | + # from changing it |
1707 | + if os.path.exists('/usr/bin/pip.save'): |
1708 | + shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') |
1709 | + os.remove('/usr/bin/pip.save') |
1710 | + os.remove('/root/.pydistutils.cfg') |
1711 | + # flag us as having already bootstrapped so we don't do it again |
1712 | + open('wheelhouse/.bootstrapped', 'w').close() |
1713 | + # Ensure that the newly bootstrapped libs are available. |
1714 | + # Note: this only seems to be an issue with namespace packages. |
1715 | + # Non-namespace-package libs (e.g., charmhelpers) are available |
1716 | + # without having to reload the interpreter. :/ |
1717 | + reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0]) |
1718 | + |
1719 | + |
1720 | +def reload_interpreter(python): |
1721 | + """ |
1722 | + Reload the python interpreter to ensure that all deps are available. |
1723 | + |
1724 | + Newly installed modules in namespace packages sometimes seemt to |
1725 | + not be picked up by Python 3. |
1726 | + """ |
1727 | + os.execle(python, python, sys.argv[0], os.environ) |
1728 | + |
1729 | + |
1730 | +def apt_install(packages): |
1731 | + """ |
1732 | + Install apt packages. |
1733 | + |
1734 | + This ensures a consistent set of options that are often missed but |
1735 | + should really be set. |
1736 | + """ |
1737 | + if isinstance(packages, (str, bytes)): |
1738 | + packages = [packages] |
1739 | + |
1740 | + env = os.environ.copy() |
1741 | + |
1742 | + if 'DEBIAN_FRONTEND' not in env: |
1743 | + env['DEBIAN_FRONTEND'] = 'noninteractive' |
1744 | + |
1745 | + cmd = ['apt-get', |
1746 | + '--option=Dpkg::Options::=--force-confold', |
1747 | + '--assume-yes', |
1748 | + 'install'] |
1749 | + check_call(cmd + packages, env=env) |
1750 | + |
1751 | + |
1752 | +def init_config_states(): |
1753 | + import yaml |
1754 | + from charmhelpers.core import hookenv |
1755 | + from charms.reactive import set_state |
1756 | + from charms.reactive import toggle_state |
1757 | + config = hookenv.config() |
1758 | + config_defaults = {} |
1759 | + config_defs = {} |
1760 | + config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml') |
1761 | + if os.path.exists(config_yaml): |
1762 | + with open(config_yaml) as fp: |
1763 | + config_defs = yaml.safe_load(fp).get('options', {}) |
1764 | + config_defaults = {key: value.get('default') |
1765 | + for key, value in config_defs.items()} |
1766 | + for opt in config_defs.keys(): |
1767 | + if config.changed(opt): |
1768 | + set_state('config.changed') |
1769 | + set_state('config.changed.{}'.format(opt)) |
1770 | + toggle_state('config.set.{}'.format(opt), config.get(opt)) |
1771 | + toggle_state('config.default.{}'.format(opt), |
1772 | + config.get(opt) == config_defaults[opt]) |
1773 | + hookenv.atexit(clear_config_states) |
1774 | + |
1775 | + |
1776 | +def clear_config_states(): |
1777 | + from charmhelpers.core import hookenv, unitdata |
1778 | + from charms.reactive import remove_state |
1779 | + config = hookenv.config() |
1780 | + remove_state('config.changed') |
1781 | + for opt in config.keys(): |
1782 | + remove_state('config.changed.{}'.format(opt)) |
1783 | + remove_state('config.set.{}'.format(opt)) |
1784 | + remove_state('config.default.{}'.format(opt)) |
1785 | + unitdata.kv().flush() |
1786 | |
1787 | === added file 'deps/layer/layer-basic/lib/charms/layer/execd.py' |
1788 | --- deps/layer/layer-basic/lib/charms/layer/execd.py 1970-01-01 00:00:00 +0000 |
1789 | +++ deps/layer/layer-basic/lib/charms/layer/execd.py 2017-02-15 08:51:47 +0000 |
1790 | @@ -0,0 +1,138 @@ |
1791 | +# Copyright 2014-2016 Canonical Limited. |
1792 | +# |
1793 | +# This file is part of layer-basic, the reactive base layer for Juju. |
1794 | +# |
1795 | +# charm-helpers is free software: you can redistribute it and/or modify |
1796 | +# it under the terms of the GNU Lesser General Public License version 3 as |
1797 | +# published by the Free Software Foundation. |
1798 | +# |
1799 | +# charm-helpers is distributed in the hope that it will be useful, |
1800 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1801 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1802 | +# GNU Lesser General Public License for more details. |
1803 | +# |
1804 | +# You should have received a copy of the GNU Lesser General Public License |
1805 | +# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
1806 | + |
1807 | +# This module may only import from the Python standard library. |
1808 | +import os |
1809 | +import sys |
1810 | +import subprocess |
1811 | +import time |
1812 | + |
1813 | +''' |
1814 | +execd/preinstall |
1815 | + |
1816 | +It is often necessary to configure and reconfigure machines |
1817 | +after provisioning, but before attempting to run the charm. |
1818 | +Common examples are specialized network configuration, enabling |
1819 | +of custom hardware, non-standard disk partitioning and filesystems, |
1820 | +adding secrets and keys required for using a secured network. |
1821 | + |
1822 | +The reactive framework's base layer invokes this mechanism as |
1823 | +early as possible, before any network access is made or dependencies |
1824 | +unpacked or non-standard modules imported (including the charms.reactive |
1825 | +framework itself). |
1826 | + |
1827 | +Operators needing to use this functionality may branch a charm and |
1828 | +create an exec.d directory in it. The exec.d directory in turn contains |
1829 | +one or more subdirectories, each of which contains an executable called |
1830 | +charm-pre-install and any other required resources. The charm-pre-install |
1831 | +executables are run, and if successful, state saved so they will not be |
1832 | +run again. |
1833 | + |
1834 | + $CHARM_DIR/exec.d/mynamespace/charm-pre-install |
1835 | + |
1836 | +An alternative to branching a charm is to compose a new charm that contains |
1837 | +the exec.d directory, using the original charm as a layer, |
1838 | + |
1839 | +A charm author could also abuse this mechanism to modify the charm |
1840 | +environment in unusual ways, but for most purposes it is saner to use |
1841 | +charmhelpers.core.hookenv.atstart(). |
1842 | +''' |
1843 | + |
1844 | + |
1845 | +def default_execd_dir(): |
1846 | + return os.path.join(os.environ['CHARM_DIR'], 'exec.d') |
1847 | + |
1848 | + |
1849 | +def execd_module_paths(execd_dir=None): |
1850 | + """Generate a list of full paths to modules within execd_dir.""" |
1851 | + if not execd_dir: |
1852 | + execd_dir = default_execd_dir() |
1853 | + |
1854 | + if not os.path.exists(execd_dir): |
1855 | + return |
1856 | + |
1857 | + for subpath in os.listdir(execd_dir): |
1858 | + module = os.path.join(execd_dir, subpath) |
1859 | + if os.path.isdir(module): |
1860 | + yield module |
1861 | + |
1862 | + |
1863 | +def execd_submodule_paths(command, execd_dir=None): |
1864 | + """Generate a list of full paths to the specified command within exec_dir. |
1865 | + """ |
1866 | + for module_path in execd_module_paths(execd_dir): |
1867 | + path = os.path.join(module_path, command) |
1868 | + if os.access(path, os.X_OK) and os.path.isfile(path): |
1869 | + yield path |
1870 | + |
1871 | + |
1872 | +def execd_sentinel_path(submodule_path): |
1873 | + module_path = os.path.dirname(submodule_path) |
1874 | + execd_path = os.path.dirname(module_path) |
1875 | + module_name = os.path.basename(module_path) |
1876 | + submodule_name = os.path.basename(submodule_path) |
1877 | + return os.path.join(execd_path, |
1878 | + '.{}_{}.done'.format(module_name, submodule_name)) |
1879 | + |
1880 | + |
1881 | +def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None): |
1882 | + """Run command for each module within execd_dir which defines it.""" |
1883 | + if stderr is None: |
1884 | + stderr = sys.stdout |
1885 | + for submodule_path in execd_submodule_paths(command, execd_dir): |
1886 | + # Only run each execd once. We cannot simply run them in the |
1887 | + # install hook, as potentially storage hooks are run before that. |
1888 | + # We cannot rely on them being idempotent. |
1889 | + sentinel = execd_sentinel_path(submodule_path) |
1890 | + if os.path.exists(sentinel): |
1891 | + continue |
1892 | + |
1893 | + try: |
1894 | + subprocess.check_call([submodule_path], stderr=stderr, |
1895 | + universal_newlines=True) |
1896 | + with open(sentinel, 'w') as f: |
1897 | + f.write('{} ran successfully {}\n'.format(submodule_path, |
1898 | + time.ctime())) |
1899 | + f.write('Removing this file will cause it to be run again\n') |
1900 | + except subprocess.CalledProcessError as e: |
1901 | + # Logs get the details. We can't use juju-log, as the |
1902 | + # output may be substantial and exceed command line |
1903 | + # length limits. |
1904 | + print("ERROR ({}) running {}".format(e.returncode, e.cmd), |
1905 | + file=stderr) |
1906 | + print("STDOUT<<EOM", file=stderr) |
1907 | + print(e.output, file=stderr) |
1908 | + print("EOM", file=stderr) |
1909 | + |
1910 | + # Unit workload status gets a shorter fail message. |
1911 | + short_path = os.path.relpath(submodule_path) |
1912 | + block_msg = "Error ({}) running {}".format(e.returncode, |
1913 | + short_path) |
1914 | + try: |
1915 | + subprocess.check_call(['status-set', 'blocked', block_msg], |
1916 | + universal_newlines=True) |
1917 | + if stop_on_error: |
1918 | + sys.exit(0) # Leave unit in blocked state. |
1919 | + except Exception: |
1920 | + pass # We care about the exec.d/* failure, not status-set. |
1921 | + |
1922 | + if stop_on_error: |
1923 | + sys.exit(e.returncode or 1) # Error state for pre-1.24 Juju |
1924 | + |
1925 | + |
1926 | +def execd_preinstall(execd_dir=None): |
1927 | + """Run charm-pre-install for each module within execd_dir.""" |
1928 | + execd_run('charm-pre-install', execd_dir=execd_dir) |
1929 | |
1930 | === added file 'deps/layer/layer-basic/metadata.yaml' |
1931 | --- deps/layer/layer-basic/metadata.yaml 1970-01-01 00:00:00 +0000 |
1932 | +++ deps/layer/layer-basic/metadata.yaml 2017-02-15 08:51:47 +0000 |
1933 | @@ -0,0 +1,1 @@ |
1934 | +{} |
1935 | |
1936 | === added directory 'deps/layer/layer-basic/reactive' |
1937 | === added file 'deps/layer/layer-basic/reactive/__init__.py' |
1938 | === added file 'deps/layer/layer-basic/requirements.txt' |
1939 | --- deps/layer/layer-basic/requirements.txt 1970-01-01 00:00:00 +0000 |
1940 | +++ deps/layer/layer-basic/requirements.txt 2017-02-15 08:51:47 +0000 |
1941 | @@ -0,0 +1,2 @@ |
1942 | +flake8 |
1943 | +pytest |
1944 | |
1945 | === added file 'deps/layer/layer-basic/tox.ini' |
1946 | --- deps/layer/layer-basic/tox.ini 1970-01-01 00:00:00 +0000 |
1947 | +++ deps/layer/layer-basic/tox.ini 2017-02-15 08:51:47 +0000 |
1948 | @@ -0,0 +1,12 @@ |
1949 | +[tox] |
1950 | +skipsdist=True |
1951 | +envlist = py34, py35 |
1952 | +skip_missing_interpreters = True |
1953 | + |
1954 | +[testenv] |
1955 | +commands = py.test -v |
1956 | +deps = |
1957 | + -r{toxinidir}/requirements.txt |
1958 | + |
1959 | +[flake8] |
1960 | +exclude=docs |
1961 | |
1962 | === added file 'deps/layer/layer-basic/wheelhouse.txt' |
1963 | --- deps/layer/layer-basic/wheelhouse.txt 1970-01-01 00:00:00 +0000 |
1964 | +++ deps/layer/layer-basic/wheelhouse.txt 2017-02-15 08:51:47 +0000 |
1965 | @@ -0,0 +1,3 @@ |
1966 | +pip>=7.0.0,<8.2.0 |
1967 | +charmhelpers>=0.4.0,<1.0.0 |
1968 | +charms.reactive>=0.1.0,<2.0.0 |
1969 | |
1970 | === added directory 'deps/layer/layer-leadership' |
1971 | === added file 'deps/layer/layer-leadership/.gitignore' |
1972 | --- deps/layer/layer-leadership/.gitignore 1970-01-01 00:00:00 +0000 |
1973 | +++ deps/layer/layer-leadership/.gitignore 2017-02-15 08:51:47 +0000 |
1974 | @@ -0,0 +1,2 @@ |
1975 | +*~ |
1976 | +*.pyc |
1977 | |
1978 | === added file 'deps/layer/layer-leadership/README.md' |
1979 | --- deps/layer/layer-leadership/README.md 1970-01-01 00:00:00 +0000 |
1980 | +++ deps/layer/layer-leadership/README.md 2017-02-15 08:51:47 +0000 |
1981 | @@ -0,0 +1,88 @@ |
1982 | +# Leadership Layer for Juju Charms |
1983 | + |
1984 | +The Leadership layer is for charm-tools and 'charm build', making it |
1985 | +easier for layered charms to deal with Juju leadership. |
1986 | + |
1987 | +This layer will initialize charms.reactive states, allowing you to |
1988 | +write handlers that will be activated by these states. It allows you |
1989 | +to completely avoid writing leader-elected and leader-settings-changed |
1990 | +hooks. As a simple example, these two handlers are all that is required |
1991 | +to make the leader unit generate a password if it is not already set, |
1992 | +and have the shared password stored in a file on all units: |
1993 | + |
1994 | +```python |
1995 | +import charms.leadership |
1996 | +from charmhelpers.core.host import pwgen |
1997 | + |
1998 | + |
1999 | +@when('leadership.is_leader') |
2000 | +@when_not('leadership.set.admin_password') |
2001 | +def generate_secret(): |
2002 | + charms.leadership.leader_set(admin_password=pwgen()) |
2003 | + |
2004 | + |
2005 | +@when('leadership.changed.admin_password') |
2006 | +def store_secret(): |
2007 | + write_file('/etc/foopass', leader_get('admin_password')) |
2008 | +``` |
2009 | + |
2010 | + |
2011 | +## States |
2012 | + |
2013 | +The following states are set appropriately on startup, before any @hook |
2014 | +decorated methods are invoked: |
2015 | + |
2016 | +* `leadership.is_leader` |
2017 | + |
2018 | + This state is set when the unit is the leader. The unit will remain |
2019 | + the leader for the remainder of the hook, but may not be leader in |
2020 | + future hooks. |
2021 | + |
2022 | +* `leadership.set.{varname}` |
2023 | + |
2024 | + This state is set for each leadership setting (ie. the |
2025 | + `leadership.set.foo` state will be set if the leader has set |
2026 | + the foo leadership setting to any value). It will remain |
2027 | + set for the remainder of the hook, unless the unit is the leader |
2028 | + and calls `reactive.leadership.leader_set()` and resets the value |
2029 | + to None. |
2030 | + |
2031 | +* `leadership.changed.{varname}` |
2032 | + |
2033 | + This state is set for each leadership setting that has changed |
2034 | + since the last hook. It will remain set for the remainder of the |
2035 | + hook. It will not be set in the next hook, unless the leader has |
2036 | + changed the leadership setting yet again. |
2037 | + |
2038 | +* `leadership.changed` |
2039 | + |
2040 | + One or more leadership settings has changed since the last hook. |
2041 | + This state will remain set for the remainder of the hook. It will |
2042 | + not be set in the next hook, unless the leader has made further |
2043 | + changes. |
2044 | + |
2045 | + |
2046 | +## Methods |
2047 | + |
2048 | +The `charms.leadership` module exposes the `leader_set()` and |
2049 | +`leader_get()` methods, which match the methods found in the |
2050 | +`charmhelpers.core.hookenv` module. `reactive.leadership.leader_set()` |
2051 | +should be used instead of the charmhelpers function to ensure that |
2052 | +the reactive state is updated when the leadership settings are. If you |
2053 | +do not do this, then you risk handlers waiting on these states to not |
2054 | +be run on the leader (because when the leader changes settings, it |
2055 | +triggers leader-settings-changed hooks on the follower units but |
2056 | +no hooks on itself). |
2057 | + |
2058 | + |
2059 | +## Support |
2060 | + |
2061 | +This layer is maintained on Launchpad by |
2062 | +Stuart Bishop (stuart.bishop@canonical.com). |
2063 | + |
2064 | +Code is available using git at git+ssh://git.launchpad.net/layer-leadership. |
2065 | + |
2066 | +Bug reports can be made at https://bugs.launchpad.net/layer-leadership. |
2067 | + |
2068 | +Queries and comments can be made on the Juju mailing list, Juju IRC |
2069 | +channels, or at https://answers.launchpad.net/layer-leadership. |
2070 | |
2071 | === added file 'deps/layer/layer-leadership/copyright' |
2072 | --- deps/layer/layer-leadership/copyright 1970-01-01 00:00:00 +0000 |
2073 | +++ deps/layer/layer-leadership/copyright 2017-02-15 08:51:47 +0000 |
2074 | @@ -0,0 +1,15 @@ |
2075 | +Copyright 2015-2016 Canonical Ltd. |
2076 | + |
2077 | +This file is part of the Leadership Layer for Juju. |
2078 | + |
2079 | +This program is free software: you can redistribute it and/or modify |
2080 | +it under the terms of the GNU General Public License version 3, as |
2081 | +published by the Free Software Foundation. |
2082 | + |
2083 | +This program is distributed in the hope that it will be useful, but |
2084 | +WITHOUT ANY WARRANTY; without even the implied warranties of |
2085 | +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2086 | +PURPOSE. See the GNU General Public License for more details. |
2087 | + |
2088 | +You should have received a copy of the GNU General Public License |
2089 | +along with this program. If not, see <http://www.gnu.org/licenses/>. |
2090 | |
2091 | === added file 'deps/layer/layer-leadership/layer.yaml' |
2092 | --- deps/layer/layer-leadership/layer.yaml 1970-01-01 00:00:00 +0000 |
2093 | +++ deps/layer/layer-leadership/layer.yaml 2017-02-15 08:51:47 +0000 |
2094 | @@ -0,0 +1,17 @@ |
2095 | +# Copyright 2015-2016 Canonical Ltd. |
2096 | +# |
2097 | +# This file is part of the Leadership Layer for Juju. |
2098 | +# |
2099 | +# This program is free software: you can redistribute it and/or modify |
2100 | +# it under the terms of the GNU General Public License version 3, as |
2101 | +# published by the Free Software Foundation. |
2102 | +# |
2103 | +# This program is distributed in the hope that it will be useful, but |
2104 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2105 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2106 | +# PURPOSE. See the GNU General Public License for more details. |
2107 | +# |
2108 | +# You should have received a copy of the GNU General Public License |
2109 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2110 | +includes: |
2111 | + - layer:basic |
2112 | |
2113 | === added directory 'deps/layer/layer-leadership/lib' |
2114 | === added directory 'deps/layer/layer-leadership/lib/charms' |
2115 | === added file 'deps/layer/layer-leadership/lib/charms/__init__.py' |
2116 | --- deps/layer/layer-leadership/lib/charms/__init__.py 1970-01-01 00:00:00 +0000 |
2117 | +++ deps/layer/layer-leadership/lib/charms/__init__.py 2017-02-15 08:51:47 +0000 |
2118 | @@ -0,0 +1,2 @@ |
2119 | +from pkgutil import extend_path |
2120 | +__path__ = extend_path(__path__, __name__) |
2121 | |
2122 | === added file 'deps/layer/layer-leadership/lib/charms/leadership.py' |
2123 | --- deps/layer/layer-leadership/lib/charms/leadership.py 1970-01-01 00:00:00 +0000 |
2124 | +++ deps/layer/layer-leadership/lib/charms/leadership.py 2017-02-15 08:51:47 +0000 |
2125 | @@ -0,0 +1,58 @@ |
2126 | +# Copyright 2015-2016 Canonical Ltd. |
2127 | +# |
2128 | +# This file is part of the Leadership Layer for Juju. |
2129 | +# |
2130 | +# This program is free software: you can redistribute it and/or modify |
2131 | +# it under the terms of the GNU General Public License version 3, as |
2132 | +# published by the Free Software Foundation. |
2133 | +# |
2134 | +# This program is distributed in the hope that it will be useful, but |
2135 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2136 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2137 | +# PURPOSE. See the GNU General Public License for more details. |
2138 | +# |
2139 | +# You should have received a copy of the GNU General Public License |
2140 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2141 | + |
2142 | +from charmhelpers.core import hookenv |
2143 | +from charmhelpers.core import unitdata |
2144 | + |
2145 | +from charms import reactive |
2146 | +from charms.reactive import not_unless |
2147 | + |
2148 | + |
2149 | +__all__ = ['leader_get', 'leader_set'] |
2150 | + |
2151 | + |
2152 | +@not_unless('leadership.is_leader') |
2153 | +def leader_set(settings=None, **kw): |
2154 | + '''Change leadership settings, per charmhelpers.core.hookenv.leader_set. |
2155 | + |
2156 | + The leadership.set.{key} reactive state will be set while the |
2157 | + leadership hook environment setting remains set. |
2158 | + |
2159 | + Changed leadership settings will set the leadership.changed.{key} |
2160 | + and leadership.changed states. These states will remain set until |
2161 | + the following hook. |
2162 | + |
2163 | + These state changes take effect immediately on the leader, and |
2164 | + in future hooks run on non-leaders. In this way both leaders and |
2165 | + non-leaders can share handlers, waiting on these states. |
2166 | + ''' |
2167 | + settings = settings or {} |
2168 | + settings.update(kw) |
2169 | + previous = unitdata.kv().getrange('leadership.settings.', strip=True) |
2170 | + |
2171 | + for key, value in settings.items(): |
2172 | + if value != previous.get(key): |
2173 | + reactive.set_state('leadership.changed.{}'.format(key)) |
2174 | + reactive.set_state('leadership.changed') |
2175 | + reactive.helpers.toggle_state('leadership.set.{}'.format(key), |
2176 | + value is not None) |
2177 | + hookenv.leader_set(settings) |
2178 | + unitdata.kv().update(settings, prefix='leadership.settings.') |
2179 | + |
2180 | + |
2181 | +def leader_get(attribute=None): |
2182 | + '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.''' |
2183 | + return hookenv.leader_get(attribute) |
2184 | |
2185 | === added directory 'deps/layer/layer-leadership/reactive' |
2186 | === added file 'deps/layer/layer-leadership/reactive/__init__.py' |
2187 | === added file 'deps/layer/layer-leadership/reactive/leadership.py' |
2188 | --- deps/layer/layer-leadership/reactive/leadership.py 1970-01-01 00:00:00 +0000 |
2189 | +++ deps/layer/layer-leadership/reactive/leadership.py 2017-02-15 08:51:47 +0000 |
2190 | @@ -0,0 +1,68 @@ |
2191 | +# Copyright 2015-2016 Canonical Ltd. |
2192 | +# |
2193 | +# This file is part of the Leadership Layer for Juju. |
2194 | +# |
2195 | +# This program is free software: you can redistribute it and/or modify |
2196 | +# it under the terms of the GNU General Public License version 3, as |
2197 | +# published by the Free Software Foundation. |
2198 | +# |
2199 | +# This program is distributed in the hope that it will be useful, but |
2200 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
2201 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2202 | +# PURPOSE. See the GNU General Public License for more details. |
2203 | +# |
2204 | +# You should have received a copy of the GNU General Public License |
2205 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2206 | + |
2207 | +from charmhelpers.core import hookenv |
2208 | +from charmhelpers.core import unitdata |
2209 | + |
2210 | +from charms import reactive |
2211 | +from charms.leadership import leader_get, leader_set |
2212 | + |
2213 | + |
2214 | +__all__ = ['leader_get', 'leader_set'] # Backwards compatibility |
2215 | + |
2216 | + |
2217 | +def initialize_leadership_state(): |
2218 | + '''Initialize leadership.* states from the hook environment. |
2219 | + |
2220 | + Invoked by hookenv.atstart() so states are available in |
2221 | + @hook decorated handlers. |
2222 | + ''' |
2223 | + is_leader = hookenv.is_leader() |
2224 | + if is_leader: |
2225 | + hookenv.log('Initializing Leadership Layer (is leader)') |
2226 | + else: |
2227 | + hookenv.log('Initializing Leadership Layer (is follower)') |
2228 | + |
2229 | + reactive.helpers.toggle_state('leadership.is_leader', is_leader) |
2230 | + |
2231 | + previous = unitdata.kv().getrange('leadership.settings.', strip=True) |
2232 | + current = hookenv.leader_get() |
2233 | + |
2234 | + # Handle deletions. |
2235 | + for key in set(previous.keys()) - set(current.keys()): |
2236 | + current[key] = None |
2237 | + |
2238 | + any_changed = False |
2239 | + for key, value in current.items(): |
2240 | + reactive.helpers.toggle_state('leadership.changed.{}'.format(key), |
2241 | + value != previous.get(key)) |
2242 | + if value != previous.get(key): |
2243 | + any_changed = True |
2244 | + reactive.helpers.toggle_state('leadership.set.{}'.format(key), |
2245 | + value is not None) |
2246 | + reactive.helpers.toggle_state('leadership.changed', any_changed) |
2247 | + |
2248 | + unitdata.kv().update(current, prefix='leadership.settings.') |
2249 | + |
2250 | + |
2251 | +# Per https://github.com/juju-solutions/charms.reactive/issues/33, |
2252 | +# this module may be imported multiple times so ensure the |
2253 | +# initialization hook is only registered once. I have to piggy back |
2254 | +# onto the namespace of a module imported before reactive discovery |
2255 | +# to do this. |
2256 | +if not hasattr(reactive, '_leadership_registered'): |
2257 | + hookenv.atstart(initialize_leadership_state) |
2258 | + reactive._leadership_registered = True |
2259 | |
2260 | === added directory 'deps/layer/trunk' |
2261 | === added file 'deps/layer/trunk/README.md' |
2262 | --- deps/layer/trunk/README.md 1970-01-01 00:00:00 +0000 |
2263 | +++ deps/layer/trunk/README.md 2017-02-15 08:51:47 +0000 |
2264 | @@ -0,0 +1,57 @@ |
2265 | +# IBM base layer |
2266 | + |
2267 | +The IBM base layer is a common starting point for IBM related charms. Use this |
2268 | +layer if you are building a layered charm with IBM software. |
2269 | + |
2270 | +## Usage |
2271 | + |
2272 | +This charm is a base layer and not meant to be built as a charm on its own. |
2273 | + |
2274 | +To use this layer, simply include the following in the runtime charm's |
2275 | +layer.yaml file |
2276 | + |
2277 | +```yaml |
2278 | +includes: ['layer:ibm-base'] |
2279 | +``` |
2280 | + |
2281 | +## Configuration |
2282 | + |
2283 | +**curl_url** - Location of the IBM product installation file(s). This should be a |
2284 | +URL that curl can use to download files. Multiple URLs should be separated |
2285 | +by a space. |
2286 | + |
2287 | +**NOTE**: cryptographic verification is required and must be |
2288 | +specified as part of the URL query string with the key a valid hash |
2289 | +algorithms md5, sha256, or sha512, and the the checksum value itself |
2290 | +(`http://<url>?[md5|sha256|sha512]=<checksum>`). |
2291 | + |
2292 | +For example: |
2293 | + |
2294 | +* `http://example.com/file.tgz?sha256=<sum>` |
2295 | +* `"sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>"` |
2296 | + |
2297 | +**curl_options** - The options passed to the 'curl' command when fetching files |
2298 | +from curl_url. |
2299 | + |
2300 | +For example: |
2301 | + |
2302 | +* `"-u <user:password>"` |
2303 | + |
2304 | +**license_accepted** - Some IBM charms require acceptance of a license before |
2305 | +installation can proceed. If required, setting this option to True indicates |
2306 | +that you have read and accepted the IBM terms and conditions found in the |
2307 | +license file referenced by the charm. |
2308 | + |
2309 | +## States |
2310 | + |
2311 | +**ibm-base.curl.resource.fetched** - When this state is set the IBM base layer has |
2312 | +downloaded and verified the resources configured by curl_url. If the charm |
2313 | +does not require any curl resources this state can be ignored. |
2314 | + |
2315 | +**ibm-base.license.accepted** - When this state is set the user has signified |
2316 | +their acceptance of the license found in the charm. If the charm implements |
2317 | +the Juju 'terms' feature this state can be safely ignored. |
2318 | + |
2319 | +## Contacts |
2320 | + |
2321 | +IBM Juju Support Team <jujusupp@us.ibm.com> |
2322 | |
2323 | === added file 'deps/layer/trunk/config.yaml' |
2324 | --- deps/layer/trunk/config.yaml 1970-01-01 00:00:00 +0000 |
2325 | +++ deps/layer/trunk/config.yaml 2017-02-15 08:51:47 +0000 |
2326 | @@ -0,0 +1,29 @@ |
2327 | +options: |
2328 | + curl_url: |
2329 | + type: string |
2330 | + default: '' |
2331 | + description: | |
2332 | + Location of the IBM product installation file(s). This should be a URL |
2333 | + that curl can use to download files. Multiple URLs should be separated |
2334 | + by a space. NOTE: cryptographic verification is required and must be |
2335 | + specified as part of the URL query string with the key a valid hash |
2336 | + algorithms md5, sha256, or sha512, and the the checksum value itself |
2337 | + (http://<url>?[md5|sha256|sha512]=<checksum>). |
2338 | + For example: |
2339 | + 'http://example.com/file.tgz?sha256=<sum>' |
2340 | + 'sftp://example.com/file1.tgz?md5=<sum> ftp://example.com/file2.tgz?md5=<sum>' |
2341 | + curl_opts: |
2342 | + type: string |
2343 | + default: '' |
2344 | + description: | |
2345 | + The options passed to the 'curl' command when fetching files from |
2346 | + curl_url. For example: |
2347 | + '-u <user:password>' |
2348 | + license_accepted: |
2349 | + type: boolean |
2350 | + default: False |
2351 | + description: | |
2352 | + Some IBM charms require acceptance of a license before installation |
2353 | + can proceed. If required, setting this option to True indicates that you |
2354 | + have read and accepted the IBM terms and conditions found in the license |
2355 | + file referenced by the charm. |
2356 | |
2357 | === added file 'deps/layer/trunk/layer.yaml' |
2358 | --- deps/layer/trunk/layer.yaml 1970-01-01 00:00:00 +0000 |
2359 | +++ deps/layer/trunk/layer.yaml 2017-02-15 08:51:47 +0000 |
2360 | @@ -0,0 +1,9 @@ |
2361 | +includes: |
2362 | + - 'layer:basic' |
2363 | + - 'layer:apt' |
2364 | + - 'layer:leadership' |
2365 | +options: |
2366 | + basic: |
2367 | + # Setting options for the basic layer. |
2368 | + packages: |
2369 | + - curl |
2370 | |
2371 | === added file 'deps/layer/trunk/metadata.yaml' |
2372 | --- deps/layer/trunk/metadata.yaml 1970-01-01 00:00:00 +0000 |
2373 | +++ deps/layer/trunk/metadata.yaml 2017-02-15 08:51:47 +0000 |
2374 | @@ -0,0 +1,15 @@ |
2375 | +name: ibm-base |
2376 | +summary: This layer provides a common base for IBM charms to build off of. |
2377 | +maintainer: IBM Juju Support Team <jujusupp@us.ibm.com> |
2378 | +description: | |
2379 | + This layer provides license acceptance using the terms feature, it also |
2380 | + provides apt support from the apt layer, and Juju leadership from the |
2381 | + leadership layer. |
2382 | +min-juju-version: '2.0-beta1' |
2383 | +series: |
2384 | + - trusty |
2385 | + - xenial |
2386 | +tags: |
2387 | + - ibm |
2388 | + - apt |
2389 | + - leadership |
2390 | |
2391 | === added directory 'deps/layer/trunk/reactive' |
2392 | === added file 'deps/layer/trunk/reactive/ibm-base.sh' |
2393 | --- deps/layer/trunk/reactive/ibm-base.sh 1970-01-01 00:00:00 +0000 |
2394 | +++ deps/layer/trunk/reactive/ibm-base.sh 2017-02-15 08:51:47 +0000 |
2395 | @@ -0,0 +1,107 @@ |
2396 | +#!/bin/bash |
2397 | +source charms.reactive.sh |
2398 | +set -e |
2399 | + |
2400 | + |
2401 | +# Utility function to verify a downloaded resource |
2402 | +# :param: file name |
2403 | +# :param: checksum type |
2404 | +# :param: checksum value |
2405 | +function verify_curl_resource() { |
2406 | + local FILE=$1 |
2407 | + local TYPE=$2 |
2408 | + local EXPECTED_SUM=$3 |
2409 | + local CALCULATED_SUM="" |
2410 | + local PROG="" |
2411 | + |
2412 | + if [ ! -r ${FILE} ]; then |
2413 | + status-set blocked "ibm-base: could not read ${FILE}" |
2414 | + juju-log "Could not verify the downloaded resource. File could not be read: ${FILE}" |
2415 | + fi |
2416 | + |
2417 | + # Set our checksum utility based on the requested type |
2418 | + case "${TYPE}" in |
2419 | + md5) |
2420 | + PROG='md5sum' |
2421 | + ;; |
2422 | + sha256) |
2423 | + PROG='sha256sum' |
2424 | + ;; |
2425 | + sha512) |
2426 | + PROG='sha512sum' |
2427 | + ;; |
2428 | + *) |
2429 | + status-set blocked "ibm-base: checksum type must be md5, sha215, or sha512" |
2430 | + juju-log "Could not verify the downloaded resource ${FILE}. Unknown checksum type: ${TYPE}" |
2431 | + return 1 |
2432 | + esac |
2433 | + |
2434 | + CALCULATED_SUM=`${PROG} ${FILE} | awk '{print $1}'` |
2435 | + if [ "${CALCULATED_SUM}" = "${EXPECTED_SUM}" ]; then |
2436 | + juju-log "Checksum verified for ${FILE}." |
2437 | + return 0 |
2438 | + else |
2439 | + status-set blocked "ibm-base: checksums did not match" |
2440 | + juju-log "Checksum mismatch for ${FILE}. Expected ${EXPECTED_SUM}, got ${CALCULATED_SUM}" |
2441 | + return 1 |
2442 | + fi |
2443 | +} |
2444 | + |
2445 | + |
2446 | +# Fetch curl resources if a URL is configured |
2447 | +@when 'config.set.curl_url' |
2448 | +@when_any 'config.new.curl_url' 'config.changed.curl_url' 'config.new.curl_opts' 'config.changed.curl_opts' |
2449 | +function fetch_curl_resource() { |
2450 | + local ARCHIVE_DIR="${CHARM_DIR}/files/archives" |
2451 | + local CURL_URL=$(config-get 'curl_url') |
2452 | + local CURL_OPTS=$(config-get 'curl_opts') |
2453 | + |
2454 | + status-set maintenance "ibm-base: fetching resource(s)" |
2455 | + |
2456 | + mkdir -p ${ARCHIVE_DIR} |
2457 | + cd ${ARCHIVE_DIR} |
2458 | + # Multiple URLs may be separated by a space, so loop. |
2459 | + for URL_STRING in ${CURL_URL} |
2460 | + do |
2461 | + # For each URL_STRING, set the url, checksum type, and checksum value. |
2462 | + local URL=${URL_STRING%%\?*} # string before the first '?' |
2463 | + local FILE_NAME=${URL##*\/} # string after the last '/' |
2464 | + local SUM_STRING=${URL_STRING#*\?} # string after the first '?' |
2465 | + local SUM_TYPE=${SUM_STRING%%\=*} # string before the first '=' |
2466 | + local SUM_VALUE=${SUM_STRING#*\=} # string after the first '=' |
2467 | + |
2468 | + if [ -z ${FILE_NAME} ]; then |
2469 | + FILE_NAME="juju-${RANDOM}" |
2470 | + fi |
2471 | + curl --silent --show-error ${CURL_OPTS} -o ${FILE_NAME} ${URL} |
2472 | + |
2473 | + # Verify our resource checksum. If this fails, let verify_resource log |
2474 | + # the reason and exit successfully. Exiting non-zero would fail the hook, |
2475 | + # so return 0 and simply inform the user that verification failed. |
2476 | + verify_curl_resource ${FILE_NAME} ${SUM_TYPE} ${SUM_VALUE} || return 0 |
2477 | + done |
2478 | + cd - |
2479 | + |
2480 | + set_state 'ibm-base.curl.resource.fetched' |
2481 | + status-set active "ibm-base: curl resource(s) fetched" |
2482 | + juju-log 'Curl resource fetched' |
2483 | +} |
2484 | + |
2485 | + |
2486 | +# Handle license acceptance |
2487 | +@when 'config.changed.license_accepted' |
2488 | +function check_license_acceptance() { |
2489 | + local LIC_ACCEPTED=$(config-get 'license_accepted') |
2490 | + |
2491 | + # compare lowercase LIC_ACCEPTED (requires bash > 4) |
2492 | + if [ "${LIC_ACCEPTED,,}" = "true" ]; then |
2493 | + set_state 'ibm-base.license.accepted' |
2494 | + juju-log 'License accepted' |
2495 | + else |
2496 | + remove_state 'ibm-base.license.accepted' |
2497 | + juju-log 'License NOT accepted' |
2498 | + fi |
2499 | +} |
2500 | + |
2501 | +# Main reactive entry point |
2502 | +reactive_handler_main |
2503 | |
2504 | === added directory 'hooks' |
2505 | === removed directory 'hooks' |
2506 | === added file 'hooks/config-changed' |
2507 | --- hooks/config-changed 1970-01-01 00:00:00 +0000 |
2508 | +++ hooks/config-changed 2017-02-15 08:51:47 +0000 |
2509 | @@ -0,0 +1,19 @@ |
2510 | +#!/usr/bin/env python3 |
2511 | + |
2512 | +# Load modules from $CHARM_DIR/lib |
2513 | +import sys |
2514 | +sys.path.append('lib') |
2515 | + |
2516 | +from charms.layer import basic |
2517 | +basic.bootstrap_charm_deps() |
2518 | +basic.init_config_states() |
2519 | + |
2520 | + |
2521 | +# This will load and run the appropriate @hook and other decorated |
2522 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2523 | +# and $CHARM_DIR/hooks/relations. |
2524 | +# |
2525 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2526 | +# for more information on this pattern. |
2527 | +from charms.reactive import main |
2528 | +main() |
2529 | |
2530 | === removed file 'hooks/config-changed' |
2531 | --- hooks/config-changed 2015-06-10 10:01:41 +0000 |
2532 | +++ hooks/config-changed 1970-01-01 00:00:00 +0000 |
2533 | @@ -1,242 +0,0 @@ |
2534 | -#!/bin/bash |
2535 | - |
2536 | -set -e |
2537 | -MQ_INSTALL_PATH=/opt/mqm |
2538 | -ARCHITECTURE=`uname -m` |
2539 | -RPM_INSTALL_ARG="-ivh --nodeps --force-debian" |
2540 | -if [ "$ARCHITECTURE" = "ppc64le" ]; then |
2541 | - RPM_INSTALL_ARG="-ivh --nodeps --force-debian --ignorearch" |
2542 | -fi |
2543 | - |
2544 | - |
2545 | -# Check whether MQ is installed |
2546 | -is_mq_installed() |
2547 | -{ |
2548 | - if [ -d $MQ_INSTALL_PATH/bin ]; then |
2549 | - source $MQ_INSTALL_PATH/bin/setmqenv -s |
2550 | - if [ $? == 0 ]; then |
2551 | - source $MQ_INSTALL_PATH/bin/setmqenv -s |
2552 | - echo "True" |
2553 | - fi |
2554 | - else |
2555 | - echo "False" |
2556 | - fi |
2557 | -} |
2558 | - |
2559 | -# Remove MQ, if installed |
2560 | -remove_software() |
2561 | -{ |
2562 | - |
2563 | - mq_installed=`is_mq_installed` |
2564 | - if [ $mq_installed == True ]; then |
2565 | - juju-log "Removing IBM MQ software." |
2566 | - |
2567 | - # Stop all queues |
2568 | - questr="QMNAME(" |
2569 | - for queue in `$MQ_INSTALL_PATH/bin/dspmq -o installation | cut -d' ' -f1 `; |
2570 | - do |
2571 | - # Get the queue manager name |
2572 | - queue_mgr_name=${queue:${#questr}:${#queue}-${#questr}-1} |
2573 | - juju-log "Stopping queue manager $queue_mgr_name" |
2574 | - set +e |
2575 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqm -w $queue_mgr_name" |
2576 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqlsr -w -m $queue_mgr_name" |
2577 | - set -e |
2578 | - done |
2579 | - # Get list of packages and remove them |
2580 | - mq_rpms="`rpm -qa | grep MQSeries`" |
2581 | - juju-log "Removing package(s) $mq_rpms" |
2582 | - rpm -ev --force-debian $mq_rpms |
2583 | - fi |
2584 | - |
2585 | -} |
2586 | - |
2587 | -# Remove MQ if license not accpeted |
2588 | -remove_unaccepted_software() |
2589 | -{ |
2590 | - |
2591 | - if [ $1 == False ]; then |
2592 | - juju-log "Removing IBM MQ (if installed) as the license agreement is not accepted." |
2593 | - remove_software |
2594 | - fi |
2595 | - |
2596 | -} |
2597 | - |
2598 | -# Update system configuration after installing MQ |
2599 | -configure_system() |
2600 | -{ |
2601 | - juju-log "IBM MQ: Updating system configuration." |
2602 | - |
2603 | - # Some containers do not allow system updates. |
2604 | - # Prevent the script from failing in such cases |
2605 | - set +e |
2606 | - shmmax=`sysctl kernel.shmmax | cut -d'=' -f2` |
2607 | - if [ $shmmax -lt 268435456 ]; |
2608 | - then |
2609 | - sysctl -w kernel.shmmax=268435456 |
2610 | - fi |
2611 | - |
2612 | - fmax=`sysctl fs.file-max | cut -d'=' -f2` |
2613 | - if [ $fmax -lt 524288 ]; |
2614 | - then |
2615 | - sysctl -w fs.file-max=524288 |
2616 | - fi |
2617 | - sysctl -p |
2618 | - |
2619 | - # Add user ubuntu to mqm group |
2620 | - adduser ubuntu mqm |
2621 | - |
2622 | - # Update mqm file limits |
2623 | - if [ "`grep mqm /etc/security/limits.conf`"=="" ]; then |
2624 | - sed -i 's/# End of file/mqm hard nofile 10240\nmqm soft nofile 10240\n# End of file/' /etc/security/limits.conf |
2625 | - fi |
2626 | - set -e |
2627 | - juju-log "IBM MQ: Completed system configuration update." |
2628 | - |
2629 | -} |
2630 | - |
2631 | -# Verify installation |
2632 | -verify_install() |
2633 | -{ |
2634 | - |
2635 | - juju-log "IBM MQ: Verifying installation." |
2636 | - |
2637 | - # Prevent the script from failing on failure. |
2638 | - # It could because configure_system call failed |
2639 | - set +e |
2640 | - |
2641 | - # Run as mqm user as root will not work |
2642 | - |
2643 | - # Create queue manager |
2644 | - juju-log "IBM MQ: Create queue manager QMA." |
2645 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/crtmqm QMA" |
2646 | - if [ $? == 0 ]; then |
2647 | - juju-log "IBM MQ: queue manager QMA created." |
2648 | - else |
2649 | - juju-log "IBM MQ: Failed to create queue manager QMA." |
2650 | - exit 0 |
2651 | - fi |
2652 | - |
2653 | - # Start queue manager |
2654 | - juju-log "IBM MQ: Starting queue manager QMA." |
2655 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/strmqm QMA" |
2656 | - if [ $? == 0 ]; then |
2657 | - juju-log "IBM MQ: Queue manager QMA started." |
2658 | - else |
2659 | - juju-log "IBM MQ: Failed to start queue manager QMA." |
2660 | - exit 0 |
2661 | - fi |
2662 | - |
2663 | - # Create queue |
2664 | - juju-log "IBM MQ: Creating queue." |
2665 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/runmqsc QMA < $CHARM_DIR/files/archives/mq_create_queue.mqsc" |
2666 | - if [ $? == 0 ]; then |
2667 | - juju-log "IBM MQ: Queue created." |
2668 | - else |
2669 | - juju-log "IBM MQ: Failed to create queue." |
2670 | - exit 0 |
2671 | - fi |
2672 | - |
2673 | - # Send message to queue |
2674 | - juju-log "IBM MQ: Sending message to queue." |
2675 | - su -l mqm -c "$MQ_INSTALL_PATH/samp/bin/amqsput QUEUE1 QMA < $CHARM_DIR/files/archives/qinput" |
2676 | - if [ $? == 0 ]; then |
2677 | - juju-log "IBM MQ: Message sent to queue." |
2678 | - else |
2679 | - juju-log "IBM MQ: Failed to send message to queue." |
2680 | - exit 0 |
2681 | - fi |
2682 | - |
2683 | - # Give MQ some time |
2684 | - sleep 5 |
2685 | - |
2686 | - # Receive message from queue |
2687 | - juju-log "IBM MQ: Receiving message from queue." |
2688 | - su -l mqm -c "$MQ_INSTALL_PATH/samp/bin/amqsget QUEUE1 QMA" |
2689 | - if [ $? == 0 ]; then |
2690 | - juju-log "IBM MQ: Message received from queue." |
2691 | - else |
2692 | - juju-log "IBM MQ: Failed to receive message from queue." |
2693 | - exit 0 |
2694 | - fi |
2695 | - |
2696 | - # Clean up |
2697 | - # Delete queue |
2698 | - juju-log "IBM MQ: Deleting queue." |
2699 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/runmqsc QMA < $CHARM_DIR/files/archives/mq_delete_queue.mqsc" |
2700 | - if [ $? == 0 ]; then |
2701 | - juju-log "IBM MQ: Deleted queue." |
2702 | - else |
2703 | - juju-log "IBM IB: Failed to delete queue." |
2704 | - fi |
2705 | - |
2706 | - # End queue manger |
2707 | - juju-log "IBM MQ: Stopping queue manager." |
2708 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqm QMA" |
2709 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqlsr -w -m QMA" |
2710 | - if [ $? -eq 0 ]; then |
2711 | - juju-log "IBM IB: Queue Manager is stopped." |
2712 | - else |
2713 | - juju-log "IBM IB: Queue Manager failed to stop." |
2714 | - fi |
2715 | - |
2716 | - sleep 10 |
2717 | - |
2718 | - juju-log "IBM MQ: Deleting queue manager." |
2719 | - su -l mqm -c "$MQ_INSTALL_PATH/bin/dltmqm QMA" |
2720 | - if [ $? -eq 0 ]; then |
2721 | - juju-log "IBM IB: Queue Manager is deleted." |
2722 | - else |
2723 | - juju-log "IBM IB: Queue Manager could not be deleted." |
2724 | - fi |
2725 | - |
2726 | - set -e |
2727 | - juju-log "IBM MQ: Install verification completed." |
2728 | - |
2729 | -} |
2730 | - |
2731 | - |
2732 | -juju-log "IBM MQ: Begin config-change hook" |
2733 | -mq_license_accepted=`config-get accept-ibm-mq-license` |
2734 | - |
2735 | - |
2736 | -# Remove MQ if license not accepted and exit. Else install it |
2737 | -remove_unaccepted_software $mq_license_accepted |
2738 | -if [ $mq_license_accepted == False ]; then |
2739 | - juju-log "IBM MQ License not accepted." |
2740 | - |
2741 | -elif [ $mq_license_accepted == True ]; then |
2742 | - |
2743 | - juju-log "License accepted" |
2744 | - mq_inst=`is_mq_installed` |
2745 | - if [ -f $CHARM_DIR/files/archives/*.gz ]; then |
2746 | - juju-log "IBM MQ: Extracting IBM MQ package." |
2747 | - tar xvfz $CHARM_DIR/files/archives/*.gz --strip-components=1 -C $CHARM_DIR/files/archives |
2748 | - rm -rf $CHARM_DIR/files/archives/*.gz |
2749 | - juju-log "IBM MQ: Extracted IBM MQ package." |
2750 | - juju-log "Awaiting acceptance of IBM MQ license (see README on how to accept the license)." |
2751 | - fi |
2752 | - if [ $mq_inst == False ]; then |
2753 | - # Check MQ package availability |
2754 | - if [ -f $CHARM_DIR/files/archives/MQSeriesServer*rpm ] && [ -f $CHARM_DIR/files/archives/MQSeriesRuntime*rpm ]; |
2755 | - then |
2756 | - echo "MQ Packages available for installation."; |
2757 | - $CHARM_DIR/files/archives/mqlicense.sh -accept |
2758 | - juju-log "Installing available MQ packages." |
2759 | - rpm $RPM_INSTALL_ARG --prefix $MQ_INSTALL_PATH $CHARM_DIR/files/archives/MQSeries*rpm |
2760 | - juju-log "Installation of available MQ packages complete." |
2761 | - # Configure system values for MQ |
2762 | - configure_system |
2763 | - #Verify installation |
2764 | - verify_install |
2765 | - else |
2766 | - echo "MQ Packages missing. Please check README file."; |
2767 | - echo "Upgrade MQ charm after adding the MQ packages"; |
2768 | - exit 0 |
2769 | - fi |
2770 | - fi |
2771 | -else |
2772 | - juju-log " Acceptable values for license is 'True' or 'False'" |
2773 | - |
2774 | -fi |
2775 | -juju-log "IBM MQ: End config-change hook" |
2776 | |
2777 | === added file 'hooks/hook.template' |
2778 | --- hooks/hook.template 1970-01-01 00:00:00 +0000 |
2779 | +++ hooks/hook.template 2017-02-15 08:51:47 +0000 |
2780 | @@ -0,0 +1,19 @@ |
2781 | +#!/usr/bin/env python3 |
2782 | + |
2783 | +# Load modules from $CHARM_DIR/lib |
2784 | +import sys |
2785 | +sys.path.append('lib') |
2786 | + |
2787 | +from charms.layer import basic |
2788 | +basic.bootstrap_charm_deps() |
2789 | +basic.init_config_states() |
2790 | + |
2791 | + |
2792 | +# This will load and run the appropriate @hook and other decorated |
2793 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2794 | +# and $CHARM_DIR/hooks/relations. |
2795 | +# |
2796 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2797 | +# for more information on this pattern. |
2798 | +from charms.reactive import main |
2799 | +main() |
2800 | |
2801 | === added file 'hooks/install' |
2802 | --- hooks/install 1970-01-01 00:00:00 +0000 |
2803 | +++ hooks/install 2017-02-15 08:51:47 +0000 |
2804 | @@ -0,0 +1,19 @@ |
2805 | +#!/usr/bin/env python3 |
2806 | + |
2807 | +# Load modules from $CHARM_DIR/lib |
2808 | +import sys |
2809 | +sys.path.append('lib') |
2810 | + |
2811 | +from charms.layer import basic |
2812 | +basic.bootstrap_charm_deps() |
2813 | +basic.init_config_states() |
2814 | + |
2815 | + |
2816 | +# This will load and run the appropriate @hook and other decorated |
2817 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2818 | +# and $CHARM_DIR/hooks/relations. |
2819 | +# |
2820 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2821 | +# for more information on this pattern. |
2822 | +from charms.reactive import main |
2823 | +main() |
2824 | |
2825 | === removed file 'hooks/install' |
2826 | --- hooks/install 2015-06-10 10:01:41 +0000 |
2827 | +++ hooks/install 1970-01-01 00:00:00 +0000 |
2828 | @@ -1,30 +0,0 @@ |
2829 | -#!/bin/bash |
2830 | -set -e |
2831 | -#Install RPM dependency |
2832 | -juju-log "IBM MQ: Begin Install." |
2833 | - |
2834 | -# Get the architecture from the uname command. |
2835 | -ARCHITECTURE=`uname -m` |
2836 | -if [ "$ARCHITECTURE" != "x86_64" -a "$ARCHITECTURE" != "ppc64le" ]; then |
2837 | - juju-log "IBM MQ: Unsupported platform. IBM MQ installed with this Charm supports only the x86_64 and POWER LE (ppc64le) platforms." |
2838 | - exit 1 |
2839 | -fi |
2840 | - |
2841 | -# Install RPM |
2842 | -juju-log "IBM MQ: Downloading and installng RPM." |
2843 | -apt-get install -y rpm |
2844 | -juju-log "IBM MQ: RPM downloaded and installed." |
2845 | - |
2846 | -# Extract IBM MQ archive file, if it exists to files/archives |
2847 | -if [ -f $CHARM_DIR/files/archives/*.gz ]; then |
2848 | - juju-log "IBM MQ: Extracting IBM MQ package." |
2849 | - tar xvfz $CHARM_DIR/files/archives/*.gz --strip-components=1 -C $CHARM_DIR/files/archives |
2850 | - rm -rf $CHARM_DIR/files/archives/*.gz |
2851 | - juju-log "IBM MQ: Extracted IBM MQ package." |
2852 | - juju-log "Awaiting acceptance of IBM MQ license (see README on how to accept the license)." |
2853 | -else |
2854 | - juju-log "IBM MQ: IBM MQ packages not available. Refer the README on how to add it. Deploy the charm again after adding the package." |
2855 | -fi |
2856 | - |
2857 | -juju-log "IBM MQ: End Install." |
2858 | - |
2859 | |
2860 | === added file 'hooks/leader-elected' |
2861 | --- hooks/leader-elected 1970-01-01 00:00:00 +0000 |
2862 | +++ hooks/leader-elected 2017-02-15 08:51:47 +0000 |
2863 | @@ -0,0 +1,19 @@ |
2864 | +#!/usr/bin/env python3 |
2865 | + |
2866 | +# Load modules from $CHARM_DIR/lib |
2867 | +import sys |
2868 | +sys.path.append('lib') |
2869 | + |
2870 | +from charms.layer import basic |
2871 | +basic.bootstrap_charm_deps() |
2872 | +basic.init_config_states() |
2873 | + |
2874 | + |
2875 | +# This will load and run the appropriate @hook and other decorated |
2876 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2877 | +# and $CHARM_DIR/hooks/relations. |
2878 | +# |
2879 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2880 | +# for more information on this pattern. |
2881 | +from charms.reactive import main |
2882 | +main() |
2883 | |
2884 | === added file 'hooks/leader-settings-changed' |
2885 | --- hooks/leader-settings-changed 1970-01-01 00:00:00 +0000 |
2886 | +++ hooks/leader-settings-changed 2017-02-15 08:51:47 +0000 |
2887 | @@ -0,0 +1,19 @@ |
2888 | +#!/usr/bin/env python3 |
2889 | + |
2890 | +# Load modules from $CHARM_DIR/lib |
2891 | +import sys |
2892 | +sys.path.append('lib') |
2893 | + |
2894 | +from charms.layer import basic |
2895 | +basic.bootstrap_charm_deps() |
2896 | +basic.init_config_states() |
2897 | + |
2898 | + |
2899 | +# This will load and run the appropriate @hook and other decorated |
2900 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2901 | +# and $CHARM_DIR/hooks/relations. |
2902 | +# |
2903 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2904 | +# for more information on this pattern. |
2905 | +from charms.reactive import main |
2906 | +main() |
2907 | |
2908 | === added file 'hooks/messaging-relation-broken' |
2909 | --- hooks/messaging-relation-broken 1970-01-01 00:00:00 +0000 |
2910 | +++ hooks/messaging-relation-broken 2017-02-15 08:51:47 +0000 |
2911 | @@ -0,0 +1,19 @@ |
2912 | +#!/usr/bin/env python3 |
2913 | + |
2914 | +# Load modules from $CHARM_DIR/lib |
2915 | +import sys |
2916 | +sys.path.append('lib') |
2917 | + |
2918 | +from charms.layer import basic |
2919 | +basic.bootstrap_charm_deps() |
2920 | +basic.init_config_states() |
2921 | + |
2922 | + |
2923 | +# This will load and run the appropriate @hook and other decorated |
2924 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2925 | +# and $CHARM_DIR/hooks/relations. |
2926 | +# |
2927 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2928 | +# for more information on this pattern. |
2929 | +from charms.reactive import main |
2930 | +main() |
2931 | |
2932 | === added file 'hooks/messaging-relation-changed' |
2933 | --- hooks/messaging-relation-changed 1970-01-01 00:00:00 +0000 |
2934 | +++ hooks/messaging-relation-changed 2017-02-15 08:51:47 +0000 |
2935 | @@ -0,0 +1,19 @@ |
2936 | +#!/usr/bin/env python3 |
2937 | + |
2938 | +# Load modules from $CHARM_DIR/lib |
2939 | +import sys |
2940 | +sys.path.append('lib') |
2941 | + |
2942 | +from charms.layer import basic |
2943 | +basic.bootstrap_charm_deps() |
2944 | +basic.init_config_states() |
2945 | + |
2946 | + |
2947 | +# This will load and run the appropriate @hook and other decorated |
2948 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2949 | +# and $CHARM_DIR/hooks/relations. |
2950 | +# |
2951 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2952 | +# for more information on this pattern. |
2953 | +from charms.reactive import main |
2954 | +main() |
2955 | |
2956 | === added file 'hooks/messaging-relation-departed' |
2957 | --- hooks/messaging-relation-departed 1970-01-01 00:00:00 +0000 |
2958 | +++ hooks/messaging-relation-departed 2017-02-15 08:51:47 +0000 |
2959 | @@ -0,0 +1,19 @@ |
2960 | +#!/usr/bin/env python3 |
2961 | + |
2962 | +# Load modules from $CHARM_DIR/lib |
2963 | +import sys |
2964 | +sys.path.append('lib') |
2965 | + |
2966 | +from charms.layer import basic |
2967 | +basic.bootstrap_charm_deps() |
2968 | +basic.init_config_states() |
2969 | + |
2970 | + |
2971 | +# This will load and run the appropriate @hook and other decorated |
2972 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2973 | +# and $CHARM_DIR/hooks/relations. |
2974 | +# |
2975 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
2976 | +# for more information on this pattern. |
2977 | +from charms.reactive import main |
2978 | +main() |
2979 | |
2980 | === added file 'hooks/messaging-relation-joined' |
2981 | --- hooks/messaging-relation-joined 1970-01-01 00:00:00 +0000 |
2982 | +++ hooks/messaging-relation-joined 2017-02-15 08:51:47 +0000 |
2983 | @@ -0,0 +1,19 @@ |
2984 | +#!/usr/bin/env python3 |
2985 | + |
2986 | +# Load modules from $CHARM_DIR/lib |
2987 | +import sys |
2988 | +sys.path.append('lib') |
2989 | + |
2990 | +from charms.layer import basic |
2991 | +basic.bootstrap_charm_deps() |
2992 | +basic.init_config_states() |
2993 | + |
2994 | + |
2995 | +# This will load and run the appropriate @hook and other decorated |
2996 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
2997 | +# and $CHARM_DIR/hooks/relations. |
2998 | +# |
2999 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
3000 | +# for more information on this pattern. |
3001 | +from charms.reactive import main |
3002 | +main() |
3003 | |
3004 | === removed file 'hooks/messaging-relation-joined' |
3005 | --- hooks/messaging-relation-joined 2015-05-03 16:45:29 +0000 |
3006 | +++ hooks/messaging-relation-joined 1970-01-01 00:00:00 +0000 |
3007 | @@ -1,12 +0,0 @@ |
3008 | -#!/bin/bash |
3009 | - |
3010 | -juju-log "IBM MQ: Begin messaging-relation-joined hook." |
3011 | -set -e |
3012 | -mq_license_accepted=`config-get accept-ibm-mq-license` |
3013 | -if [ "$mq_license_accepted" == "False" ]; then |
3014 | - juju-log "IBM MQ: IBM MQ License is not accepted." |
3015 | - juju-log "Delete the relation. Accept the IBM MQ License, as per the README, before setting up any relation." |
3016 | - exit 0 |
3017 | -fi |
3018 | -relation-set hostname=`unit-get private-address` port=1414 |
3019 | -juju-log "IBM MQ: End messaging-relation-joined hook." |
3020 | |
3021 | === added directory 'hooks/relations' |
3022 | === added directory 'hooks/relations/ibm-mq' |
3023 | === added file 'hooks/relations/ibm-mq/README.md' |
3024 | --- hooks/relations/ibm-mq/README.md 1970-01-01 00:00:00 +0000 |
3025 | +++ hooks/relations/ibm-mq/README.md 2017-02-15 08:51:47 +0000 |
3026 | @@ -0,0 +1,42 @@ |
3027 | +Overview |
3028 | +----------- |
3029 | + |
3030 | +This interface layer handles the communication between `IBM MQ` and other charms that tries to communicate by sending each other data in messages through IBM-MQ rather than by calling each other directly. |
3031 | +The provider end of this interface provides the Queue Manager name, Queue Name, IP Address and Port. |
3032 | +The consumer/requires part will be any other charm and can get all the above values viz. Queue Manager name, Queue Name, IP Address and Port to send or receive messages. Here we are describing consumer charm as `ibm-was-base` |
3033 | + |
3034 | + |
3035 | +Usage |
3036 | +------ |
3037 | + |
3038 | +#### Provides |
3039 | +IBM MQ product will provide this interface. This interface layer will set the following states, as appropriate: |
3040 | + |
3041 | + - `{relation_name}.connected`: The relation is established, IBM-MQ is ready to send it's information. |
3042 | + |
3043 | + - `get_consumer_hostname()` - returns/gets the consumer hostname to create a channel authentication rule that allows client system to use the channel by entering the MQSC command. |
3044 | + |
3045 | + - `set_mq_details()` - Sets Queue Manager name, Queue Name, IP Address and Port for the connected services. |
3046 | + As a Queue Name it sets a filename where MQSC Commands can be edited for further operation. |
3047 | + |
3048 | + |
3049 | + - `{relation_name}.departed` : The relation has been removed. Any cleanup related to the consumer charm (e.g IBM WebSphere Server) should happen now on the Consumer charm since the consumer is going away. |
3050 | + |
3051 | +#### Requires |
3052 | + |
3053 | +Consumer charms e.g `IBM WebSphere Application Server` will require this interface to connect to IBM-MQ so that they can get the necessary information about `IBM-MQ` to send/receive messages to other Applications. |
3054 | +This interface layer will set the following states, as appropriate: |
3055 | + |
3056 | +- `{relation_name}.connected` : The consumer charm has been related to a IBM-MQ provider charm. |
3057 | + At this point, the charm waits for Provider charm to send details like Queue Manager name, Queue Name, IP Address and Port. |
3058 | + |
3059 | + - `set_hostname()` - sets the `hostname`, so that IBM-MQ can get the consumer/client hostname to allow to send/receive messages. |
3060 | + |
3061 | +- `{relation_name}.ready` : The consumer charm e.g `IBM WebSphere Application Server` is now ready to access the details of IBM-MQ to send/recieve messages. Such as Queue Manager name, Queue Name, IP Address/hostname and Port. |
3062 | + |
3063 | + - `get_qm_name()` - returns the `QM_Name` that IBM-MQ has created. |
3064 | + - `get_qname()` - returns the `QName` that IBM-MQ has created. |
3065 | + - `get_mq_hostname()` - returns the `hostname` that IBM-MQ has created. |
3066 | + - `get_mq_port()` - returns the `MQ Port` that IBM-MQ has created. |
3067 | + |
3068 | +- `{relation_name}.departed` : The relation has been removed. Any cleanup related to the provider charm should happen now. |
3069 | |
3070 | === added file 'hooks/relations/ibm-mq/__init__.py' |
3071 | === added file 'hooks/relations/ibm-mq/interface.yaml' |
3072 | --- hooks/relations/ibm-mq/interface.yaml 1970-01-01 00:00:00 +0000 |
3073 | +++ hooks/relations/ibm-mq/interface.yaml 2017-02-15 08:51:47 +0000 |
3074 | @@ -0,0 +1,4 @@ |
3075 | +name: ibm-mq |
3076 | +summary: Interface for relating to ibm-mq |
3077 | +version: 1 |
3078 | +maintainer: "Juju Support <jujusupp@us.ibm.com>" |
3079 | \ No newline at end of file |
3080 | |
3081 | === added file 'hooks/relations/ibm-mq/provides.py' |
3082 | --- hooks/relations/ibm-mq/provides.py 1970-01-01 00:00:00 +0000 |
3083 | +++ hooks/relations/ibm-mq/provides.py 2017-02-15 08:51:47 +0000 |
3084 | @@ -0,0 +1,50 @@ |
3085 | +from charms.reactive import hook |
3086 | +from charms.reactive import RelationBase |
3087 | +from charms.reactive import scopes |
3088 | + |
3089 | + |
3090 | +class mqProvides(RelationBase): |
3091 | + # Every unit connecting will get the same information |
3092 | + scope = scopes.SERVICE |
3093 | + |
3094 | + @hook('{provides:ibm-mq}-relation-joined') |
3095 | + def joined(self): |
3096 | + conversation = self.conversation() |
3097 | + conversation.remove_state('{relation_name}.departed') |
3098 | + conversation.set_state('{relation_name}.connected') |
3099 | + |
3100 | + @hook('{provides:ibm-mq}-relation-departed') |
3101 | + def departed(self): |
3102 | + conversation = self.conversation() |
3103 | + conversation.remove_state('{relation_name}.connected') |
3104 | + conversation.set_state('{relation_name}.departed') |
3105 | + |
3106 | + def dismiss(self, service): |
3107 | + conversation = self.conversation(service) |
3108 | + conversation.remove_state('{relation_name}.departed') |
3109 | + |
3110 | + def reset_states(self, service): |
3111 | + conversation = self.conversation(service) |
3112 | + conversation.remove_state('{relation_name}.connected') |
3113 | + conversation.remove_state('{relation_name}.departed') |
3114 | + |
3115 | + def set_mq_details(self, service, QM_Name, Qname, host, port): |
3116 | + conversation = self.conversation(service) |
3117 | + conversation.set_remote(data={ |
3118 | + 'QM_Name': QM_Name, |
3119 | + 'Qname': Qname, |
3120 | + 'host': host, |
3121 | + 'port': port, |
3122 | + }) |
3123 | + |
3124 | + def get_consumer_hostname(self): |
3125 | + return self.get_remote('host') |
3126 | + |
3127 | + def services(self): |
3128 | + """ |
3129 | + Return a list of services requesting MQ. |
3130 | + """ |
3131 | + service = [] |
3132 | + for conversation in self.conversations(): |
3133 | + service.append(conversation.scope) |
3134 | + return service |
3135 | |
3136 | === added file 'hooks/relations/ibm-mq/requires.py' |
3137 | --- hooks/relations/ibm-mq/requires.py 1970-01-01 00:00:00 +0000 |
3138 | +++ hooks/relations/ibm-mq/requires.py 2017-02-15 08:51:47 +0000 |
3139 | @@ -0,0 +1,42 @@ |
3140 | +from charms.reactive import hook |
3141 | +from charms.reactive import RelationBase |
3142 | +from charms.reactive import scopes |
3143 | + |
3144 | + |
3145 | +class mq1Requires(RelationBase): |
3146 | + scope = scopes.GLOBAL |
3147 | + |
3148 | + @hook('{requires:ibm-mq}-relation-joined') |
3149 | + def joined(self): |
3150 | + self.remove_state('{relation_name}.departed') |
3151 | + self.set_state('{relation_name}.connected') |
3152 | + |
3153 | + @hook('{requires:ibm-mq}-relation-changed') |
3154 | + def changed(self): |
3155 | + if str(self.get_remote('port')) != "None": |
3156 | + self.set_state('{relation_name}.ready') |
3157 | + print("Status is relation_name.ready in requires") |
3158 | + |
3159 | + @hook('{requires:ibm-mq}-relation-departed') |
3160 | + def departed(self): |
3161 | + self.remove_state('{relation_name}.connected') |
3162 | + self.remove_state('{relation_name}.ready') |
3163 | + self.set_state('{relation_name}.departed') |
3164 | + |
3165 | + def set_hostname(self, host): |
3166 | + conversation = self.conversation() |
3167 | + conversation.set_remote(data={ |
3168 | + 'host': host, |
3169 | + }) |
3170 | + |
3171 | + def get_qm_name(self): |
3172 | + return self.get_remote('QM_Name') |
3173 | + |
3174 | + def get_qname(self): |
3175 | + return self.get_remote('Qname') |
3176 | + |
3177 | + def get_mq_hostname(self): |
3178 | + return self.get_remote('host') |
3179 | + |
3180 | + def get_mq_port(self): |
3181 | + return self.get_remote('port') |
3182 | |
3183 | === added file 'hooks/start' |
3184 | --- hooks/start 1970-01-01 00:00:00 +0000 |
3185 | +++ hooks/start 2017-02-15 08:51:47 +0000 |
3186 | @@ -0,0 +1,19 @@ |
3187 | +#!/usr/bin/env python3 |
3188 | + |
3189 | +# Load modules from $CHARM_DIR/lib |
3190 | +import sys |
3191 | +sys.path.append('lib') |
3192 | + |
3193 | +from charms.layer import basic |
3194 | +basic.bootstrap_charm_deps() |
3195 | +basic.init_config_states() |
3196 | + |
3197 | + |
3198 | +# This will load and run the appropriate @hook and other decorated |
3199 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
3200 | +# and $CHARM_DIR/hooks/relations. |
3201 | +# |
3202 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
3203 | +# for more information on this pattern. |
3204 | +from charms.reactive import main |
3205 | +main() |
3206 | |
3207 | === removed file 'hooks/start' |
3208 | --- hooks/start 2015-03-06 08:32:33 +0000 |
3209 | +++ hooks/start 1970-01-01 00:00:00 +0000 |
3210 | @@ -1,8 +0,0 @@ |
3211 | -#!/bin/bash |
3212 | - |
3213 | -# Juju command to open port |
3214 | - |
3215 | -set -e |
3216 | -juju-log "IBM MQ: Begin Start hook" |
3217 | -open-port 1414/TCP |
3218 | -juju-log "IBM MQ: End Start hook" |
3219 | |
3220 | === added file 'hooks/stop' |
3221 | --- hooks/stop 1970-01-01 00:00:00 +0000 |
3222 | +++ hooks/stop 2017-02-15 08:51:47 +0000 |
3223 | @@ -0,0 +1,19 @@ |
3224 | +#!/usr/bin/env python3 |
3225 | + |
3226 | +# Load modules from $CHARM_DIR/lib |
3227 | +import sys |
3228 | +sys.path.append('lib') |
3229 | + |
3230 | +from charms.layer import basic |
3231 | +basic.bootstrap_charm_deps() |
3232 | +basic.init_config_states() |
3233 | + |
3234 | + |
3235 | +# This will load and run the appropriate @hook and other decorated |
3236 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
3237 | +# and $CHARM_DIR/hooks/relations. |
3238 | +# |
3239 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
3240 | +# for more information on this pattern. |
3241 | +from charms.reactive import main |
3242 | +main() |
3243 | |
3244 | === removed file 'hooks/stop' |
3245 | --- hooks/stop 2015-03-06 08:32:33 +0000 |
3246 | +++ hooks/stop 1970-01-01 00:00:00 +0000 |
3247 | @@ -1,6 +0,0 @@ |
3248 | -#!/bin/bash |
3249 | - |
3250 | -set -e |
3251 | -juju-log "IBM MQ: Begin Stop hook" |
3252 | -close-port 1414/TCP |
3253 | -juju-log "IBM MQ: End Stop hook" |
3254 | |
3255 | === added file 'hooks/update-status' |
3256 | --- hooks/update-status 1970-01-01 00:00:00 +0000 |
3257 | +++ hooks/update-status 2017-02-15 08:51:47 +0000 |
3258 | @@ -0,0 +1,19 @@ |
3259 | +#!/usr/bin/env python3 |
3260 | + |
3261 | +# Load modules from $CHARM_DIR/lib |
3262 | +import sys |
3263 | +sys.path.append('lib') |
3264 | + |
3265 | +from charms.layer import basic |
3266 | +basic.bootstrap_charm_deps() |
3267 | +basic.init_config_states() |
3268 | + |
3269 | + |
3270 | +# This will load and run the appropriate @hook and other decorated |
3271 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
3272 | +# and $CHARM_DIR/hooks/relations. |
3273 | +# |
3274 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
3275 | +# for more information on this pattern. |
3276 | +from charms.reactive import main |
3277 | +main() |
3278 | |
3279 | === added file 'hooks/upgrade-charm' |
3280 | --- hooks/upgrade-charm 1970-01-01 00:00:00 +0000 |
3281 | +++ hooks/upgrade-charm 2017-02-15 08:51:47 +0000 |
3282 | @@ -0,0 +1,28 @@ |
3283 | +#!/usr/bin/env python3 |
3284 | + |
3285 | +# Load modules from $CHARM_DIR/lib |
3286 | +import os |
3287 | +import sys |
3288 | +sys.path.append('lib') |
3289 | + |
3290 | +# This is an upgrade-charm context, make sure we install latest deps |
3291 | +if not os.path.exists('wheelhouse/.upgrade'): |
3292 | + open('wheelhouse/.upgrade', 'w').close() |
3293 | + if os.path.exists('wheelhouse/.bootstrapped'): |
3294 | + os.unlink('wheelhouse/.bootstrapped') |
3295 | +else: |
3296 | + os.unlink('wheelhouse/.upgrade') |
3297 | + |
3298 | +from charms.layer import basic |
3299 | +basic.bootstrap_charm_deps() |
3300 | +basic.init_config_states() |
3301 | + |
3302 | + |
3303 | +# This will load and run the appropriate @hook and other decorated |
3304 | +# handlers from $CHARM_DIR/reactive, $CHARM_DIR/hooks/reactive, |
3305 | +# and $CHARM_DIR/hooks/relations. |
3306 | +# |
3307 | +# See https://jujucharms.com/docs/stable/authors-charm-building |
3308 | +# for more information on this pattern. |
3309 | +from charms.reactive import main |
3310 | +main() |
3311 | |
3312 | === added file 'layer.yaml' |
3313 | --- layer.yaml 1970-01-01 00:00:00 +0000 |
3314 | +++ layer.yaml 2017-02-15 08:51:47 +0000 |
3315 | @@ -0,0 +1,21 @@ |
3316 | +"options": |
3317 | + "basic": |
3318 | + "packages": |
3319 | + - "curl" |
3320 | + - "rpm" |
3321 | + "use_venv": !!bool "false" |
3322 | + "include_system_packages": !!bool "false" |
3323 | + "ibm-base": {} |
3324 | + "leadership": {} |
3325 | + "ibm-mq": {} |
3326 | + "apt": |
3327 | + "packages": [] |
3328 | +"includes": |
3329 | +- "layer:basic" |
3330 | +- "layer:basic" |
3331 | +- "layer:apt" |
3332 | +- "layer:leadership" |
3333 | +- "layer:ibm-base" |
3334 | +- "interface:ibm-mq" |
3335 | +"repo": "bzr+ssh://bazaar.launchpad.net/~ibmcharmers/charms/trusty/layer-ibm-mq/trunk/" |
3336 | +"is": "ibm-mq" |
3337 | |
3338 | === added directory 'lib' |
3339 | === added directory 'lib/charms' |
3340 | === added file 'lib/charms/__init__.py' |
3341 | --- lib/charms/__init__.py 1970-01-01 00:00:00 +0000 |
3342 | +++ lib/charms/__init__.py 2017-02-15 08:51:47 +0000 |
3343 | @@ -0,0 +1,2 @@ |
3344 | +from pkgutil import extend_path |
3345 | +__path__ = extend_path(__path__, __name__) |
3346 | |
3347 | === added file 'lib/charms/apt.py' |
3348 | --- lib/charms/apt.py 1970-01-01 00:00:00 +0000 |
3349 | +++ lib/charms/apt.py 2017-02-15 08:51:47 +0000 |
3350 | @@ -0,0 +1,182 @@ |
3351 | +# Copyright 2015-2016 Canonical Ltd. |
3352 | +# |
3353 | +# This file is part of the Apt layer for Juju. |
3354 | +# |
3355 | +# This program is free software: you can redistribute it and/or modify |
3356 | +# it under the terms of the GNU General Public License version 3, as |
3357 | +# published by the Free Software Foundation. |
3358 | +# |
3359 | +# This program is distributed in the hope that it will be useful, but |
3360 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
3361 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3362 | +# PURPOSE. See the GNU General Public License for more details. |
3363 | +# |
3364 | +# You should have received a copy of the GNU General Public License |
3365 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3366 | + |
3367 | +''' |
3368 | +charms.reactive helpers for dealing with deb packages. |
3369 | + |
3370 | +Add apt package sources using add_source(). Queue deb packages for |
3371 | +installation with install(). Configure and work with your software |
3372 | +once the apt.installed.{packagename} state is set. |
3373 | +''' |
3374 | +import itertools |
3375 | +import subprocess |
3376 | + |
3377 | +from charmhelpers import fetch |
3378 | +from charmhelpers.core import hookenv, unitdata |
3379 | +from charms import reactive |
3380 | + |
3381 | + |
3382 | +__all__ = ['add_source', 'update', 'queue_install', 'install_queued', |
3383 | + 'installed', 'purge', 'ensure_package_status'] |
3384 | + |
3385 | + |
3386 | +def add_source(source, key=None): |
3387 | + '''Add an apt source. |
3388 | + |
3389 | + Sets the apt.needs_update state. |
3390 | + |
3391 | + A source may be either a line that can be added directly to |
3392 | + sources.list(5), or in the form ppa:<user>/<ppa-name> for adding |
3393 | + Personal Package Archives, or a distribution component to enable. |
3394 | + |
3395 | + The package signing key should be an ASCII armoured GPG key. While |
3396 | + GPG key ids are also supported, the retrieval mechanism is insecure. |
3397 | + There is no need to specify the package signing key for PPAs or for |
3398 | + the main Ubuntu archives. |
3399 | + ''' |
3400 | + # Maybe we should remember which sources have been added already |
3401 | + # so we don't waste time re-adding them. Is this time significant? |
3402 | + fetch.add_source(source, key) |
3403 | + reactive.set_state('apt.needs_update') |
3404 | + |
3405 | + |
3406 | +def queue_install(packages, options=None): |
3407 | + """Queue one or more deb packages for install. |
3408 | + |
3409 | + The `apt.installed.{name}` state is set once the package is installed. |
3410 | + |
3411 | + If a package has already been installed it will not be reinstalled. |
3412 | + |
3413 | + If a package has already been queued it will not be requeued, and |
3414 | + the install options will not be changed. |
3415 | + |
3416 | + Sets the apt.queued_installs state. |
3417 | + """ |
3418 | + if isinstance(packages, str): |
3419 | + packages = [packages] |
3420 | + # Filter installed packages. |
3421 | + store = unitdata.kv() |
3422 | + queued_packages = store.getrange('apt.install_queue.', strip=True) |
3423 | + packages = {package: options for package in packages |
3424 | + if not (package in queued_packages or |
3425 | + reactive.helpers.is_state('apt.installed.' + package))} |
3426 | + if packages: |
3427 | + unitdata.kv().update(packages, prefix='apt.install_queue.') |
3428 | + reactive.set_state('apt.queued_installs') |
3429 | + |
3430 | + |
3431 | +def installed(): |
3432 | + '''Return the set of deb packages completed install''' |
3433 | + return set(state.split('.', 2)[2] for state in reactive.bus.get_states() |
3434 | + if state.startswith('apt.installed.')) |
3435 | + |
3436 | + |
3437 | +def purge(packages): |
3438 | + """Purge one or more deb packages from the system""" |
3439 | + fetch.apt_purge(packages, fatal=True) |
3440 | + store = unitdata.kv() |
3441 | + store.unsetrange(packages, prefix='apt.install_queue.') |
3442 | + for package in packages: |
3443 | + reactive.remove_state('apt.installed.{}'.format(package)) |
3444 | + |
3445 | + |
3446 | +def update(): |
3447 | + """Update the apt cache. |
3448 | + |
3449 | + Removes the apt.needs_update state. |
3450 | + """ |
3451 | + status_set(None, 'Updating apt cache') |
3452 | + fetch.apt_update(fatal=True) # Friends don't let friends set fatal=False |
3453 | + reactive.remove_state('apt.needs_update') |
3454 | + |
3455 | + |
3456 | +def install_queued(): |
3457 | + '''Installs queued deb packages. |
3458 | + |
3459 | + Removes the apt.queued_installs state and sets the apt.installed state. |
3460 | + |
3461 | + On failure, sets the unit's workload state to 'blocked' and returns |
3462 | + False. Package installs remain queued. |
3463 | + |
3464 | + On success, sets the apt.installed.{packagename} state for each |
3465 | + installed package and returns True. |
3466 | + ''' |
3467 | + store = unitdata.kv() |
3468 | + queue = sorted((options, package) |
3469 | + for package, options in store.getrange('apt.install_queue.', |
3470 | + strip=True).items()) |
3471 | + |
3472 | + installed = set() |
3473 | + for options, batch in itertools.groupby(queue, lambda x: x[0]): |
3474 | + packages = [b[1] for b in batch] |
3475 | + try: |
3476 | + status_set(None, 'Installing {}'.format(','.join(packages))) |
3477 | + fetch.apt_install(packages, options, fatal=True) |
3478 | + store.unsetrange(packages, prefix='apt.install_queue.') |
3479 | + installed.update(packages) |
3480 | + except subprocess.CalledProcessError: |
3481 | + status_set('blocked', |
3482 | + 'Unable to install packages {}' |
3483 | + .format(','.join(packages))) |
3484 | + return False # Without setting reactive state. |
3485 | + |
3486 | + for package in installed: |
3487 | + reactive.set_state('apt.installed.{}'.format(package)) |
3488 | + |
3489 | + reactive.remove_state('apt.queued_installs') |
3490 | + return True |
3491 | + |
3492 | + |
3493 | +def ensure_package_status(): |
3494 | + '''Hold or unhold packages per the package_status configuration option. |
3495 | + |
3496 | + All packages installed using this module and handlers are affected. |
3497 | + |
3498 | + An mechanism may be added in the future to override this for a |
3499 | + subset of installed packages. |
3500 | + ''' |
3501 | + packages = installed() |
3502 | + if not packages: |
3503 | + return |
3504 | + config = hookenv.config() |
3505 | + package_status = config['package_status'] |
3506 | + changed = reactive.helpers.data_changed('apt.package_status', |
3507 | + (package_status, sorted(packages))) |
3508 | + if changed: |
3509 | + if package_status == 'hold': |
3510 | + hookenv.log('Holding packages {}'.format(','.join(packages))) |
3511 | + fetch.apt_hold(packages) |
3512 | + else: |
3513 | + hookenv.log('Unholding packages {}'.format(','.join(packages))) |
3514 | + fetch.apt_unhold(packages) |
3515 | + reactive.remove_state('apt.needs_hold') |
3516 | + |
3517 | + |
3518 | +def status_set(state, message): |
3519 | + """Set the unit's workload status. |
3520 | + |
3521 | + Set state == None to keep the same state and just change the message. |
3522 | + """ |
3523 | + if state is None: |
3524 | + state = hookenv.status_get()[0] |
3525 | + if state == 'unknown': |
3526 | + state = 'maintenance' # Guess |
3527 | + if state in ('error', 'blocked'): |
3528 | + lvl = hookenv.WARNING |
3529 | + else: |
3530 | + lvl = hookenv.INFO |
3531 | + hookenv.status_set(state, message) |
3532 | + hookenv.log('{}: {}'.format(state, message), lvl) |
3533 | |
3534 | === added directory 'lib/charms/layer' |
3535 | === added file 'lib/charms/layer/__init__.py' |
3536 | --- lib/charms/layer/__init__.py 1970-01-01 00:00:00 +0000 |
3537 | +++ lib/charms/layer/__init__.py 2017-02-15 08:51:47 +0000 |
3538 | @@ -0,0 +1,21 @@ |
3539 | +import os |
3540 | + |
3541 | + |
3542 | +class LayerOptions(dict): |
3543 | + def __init__(self, layer_file, section=None): |
3544 | + import yaml # defer, might not be available until bootstrap |
3545 | + with open(layer_file) as f: |
3546 | + layer = yaml.safe_load(f.read()) |
3547 | + opts = layer.get('options', {}) |
3548 | + if section and section in opts: |
3549 | + super(LayerOptions, self).__init__(opts.get(section)) |
3550 | + else: |
3551 | + super(LayerOptions, self).__init__(opts) |
3552 | + |
3553 | + |
3554 | +def options(section=None, layer_file=None): |
3555 | + if not layer_file: |
3556 | + base_dir = os.environ.get('CHARM_DIR', os.getcwd()) |
3557 | + layer_file = os.path.join(base_dir, 'layer.yaml') |
3558 | + |
3559 | + return LayerOptions(layer_file, section) |
3560 | |
3561 | === added file 'lib/charms/layer/basic.py' |
3562 | --- lib/charms/layer/basic.py 1970-01-01 00:00:00 +0000 |
3563 | +++ lib/charms/layer/basic.py 2017-02-15 08:51:47 +0000 |
3564 | @@ -0,0 +1,159 @@ |
3565 | +import os |
3566 | +import sys |
3567 | +import shutil |
3568 | +import platform |
3569 | +from glob import glob |
3570 | +from subprocess import check_call |
3571 | + |
3572 | +from charms.layer.execd import execd_preinstall |
3573 | + |
3574 | + |
3575 | +def bootstrap_charm_deps(): |
3576 | + """ |
3577 | + Set up the base charm dependencies so that the reactive system can run. |
3578 | + """ |
3579 | + # execd must happen first, before any attempt to install packages or |
3580 | + # access the network, because sites use this hook to do bespoke |
3581 | + # configuration and install secrets so the rest of this bootstrap |
3582 | + # and the charm itself can actually succeed. This call does nothing |
3583 | + # unless the operator has created and populated $CHARM_DIR/exec.d. |
3584 | + execd_preinstall() |
3585 | + # ensure that $CHARM_DIR/bin is on the path, for helper scripts |
3586 | + os.environ['PATH'] += ':%s' % os.path.join(os.environ['CHARM_DIR'], 'bin') |
3587 | + venv = os.path.abspath('../.venv') |
3588 | + vbin = os.path.join(venv, 'bin') |
3589 | + vpip = os.path.join(vbin, 'pip') |
3590 | + vpy = os.path.join(vbin, 'python') |
3591 | + if os.path.exists('wheelhouse/.bootstrapped'): |
3592 | + from charms import layer |
3593 | + cfg = layer.options('basic') |
3594 | + if cfg.get('use_venv') and '.venv' not in sys.executable: |
3595 | + # activate the venv |
3596 | + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) |
3597 | + reload_interpreter(vpy) |
3598 | + return |
3599 | + # bootstrap wheelhouse |
3600 | + if os.path.exists('wheelhouse'): |
3601 | + with open('/root/.pydistutils.cfg', 'w') as fp: |
3602 | + # make sure that easy_install also only uses the wheelhouse |
3603 | + # (see https://github.com/pypa/pip/issues/410) |
3604 | + charm_dir = os.environ['CHARM_DIR'] |
3605 | + fp.writelines([ |
3606 | + "[easy_install]\n", |
3607 | + "allow_hosts = ''\n", |
3608 | + "find_links = file://{}/wheelhouse/\n".format(charm_dir), |
3609 | + ]) |
3610 | + apt_install(['python3-pip', 'python3-setuptools', 'python3-yaml']) |
3611 | + from charms import layer |
3612 | + cfg = layer.options('basic') |
3613 | + # include packages defined in layer.yaml |
3614 | + apt_install(cfg.get('packages', [])) |
3615 | + # if we're using a venv, set it up |
3616 | + if cfg.get('use_venv'): |
3617 | + if not os.path.exists(venv): |
3618 | + distname, version, series = platform.linux_distribution() |
3619 | + if series in ('precise', 'trusty'): |
3620 | + apt_install(['python-virtualenv']) |
3621 | + else: |
3622 | + apt_install(['virtualenv']) |
3623 | + cmd = ['virtualenv', '-ppython3', '--never-download', venv] |
3624 | + if cfg.get('include_system_packages'): |
3625 | + cmd.append('--system-site-packages') |
3626 | + check_call(cmd) |
3627 | + os.environ['PATH'] = ':'.join([vbin, os.environ['PATH']]) |
3628 | + pip = vpip |
3629 | + else: |
3630 | + pip = 'pip3' |
3631 | + # save a copy of system pip to prevent `pip3 install -U pip` |
3632 | + # from changing it |
3633 | + if os.path.exists('/usr/bin/pip'): |
3634 | + shutil.copy2('/usr/bin/pip', '/usr/bin/pip.save') |
3635 | + # need newer pip, to fix spurious Double Requirement error: |
3636 | + # https://github.com/pypa/pip/issues/56 |
3637 | + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse', |
3638 | + 'pip']) |
3639 | + # install the rest of the wheelhouse deps |
3640 | + check_call([pip, 'install', '-U', '--no-index', '-f', 'wheelhouse'] + |
3641 | + glob('wheelhouse/*')) |
3642 | + if not cfg.get('use_venv'): |
3643 | + # restore system pip to prevent `pip3 install -U pip` |
3644 | + # from changing it |
3645 | + if os.path.exists('/usr/bin/pip.save'): |
3646 | + shutil.copy2('/usr/bin/pip.save', '/usr/bin/pip') |
3647 | + os.remove('/usr/bin/pip.save') |
3648 | + os.remove('/root/.pydistutils.cfg') |
3649 | + # flag us as having already bootstrapped so we don't do it again |
3650 | + open('wheelhouse/.bootstrapped', 'w').close() |
3651 | + # Ensure that the newly bootstrapped libs are available. |
3652 | + # Note: this only seems to be an issue with namespace packages. |
3653 | + # Non-namespace-package libs (e.g., charmhelpers) are available |
3654 | + # without having to reload the interpreter. :/ |
3655 | + reload_interpreter(vpy if cfg.get('use_venv') else sys.argv[0]) |
3656 | + |
3657 | + |
3658 | +def reload_interpreter(python): |
3659 | + """ |
3660 | + Reload the python interpreter to ensure that all deps are available. |
3661 | + |
3662 | + Newly installed modules in namespace packages sometimes seemt to |
3663 | + not be picked up by Python 3. |
3664 | + """ |
3665 | + os.execle(python, python, sys.argv[0], os.environ) |
3666 | + |
3667 | + |
3668 | +def apt_install(packages): |
3669 | + """ |
3670 | + Install apt packages. |
3671 | + |
3672 | + This ensures a consistent set of options that are often missed but |
3673 | + should really be set. |
3674 | + """ |
3675 | + if isinstance(packages, (str, bytes)): |
3676 | + packages = [packages] |
3677 | + |
3678 | + env = os.environ.copy() |
3679 | + |
3680 | + if 'DEBIAN_FRONTEND' not in env: |
3681 | + env['DEBIAN_FRONTEND'] = 'noninteractive' |
3682 | + |
3683 | + cmd = ['apt-get', |
3684 | + '--option=Dpkg::Options::=--force-confold', |
3685 | + '--assume-yes', |
3686 | + 'install'] |
3687 | + check_call(cmd + packages, env=env) |
3688 | + |
3689 | + |
3690 | +def init_config_states(): |
3691 | + import yaml |
3692 | + from charmhelpers.core import hookenv |
3693 | + from charms.reactive import set_state |
3694 | + from charms.reactive import toggle_state |
3695 | + config = hookenv.config() |
3696 | + config_defaults = {} |
3697 | + config_defs = {} |
3698 | + config_yaml = os.path.join(hookenv.charm_dir(), 'config.yaml') |
3699 | + if os.path.exists(config_yaml): |
3700 | + with open(config_yaml) as fp: |
3701 | + config_defs = yaml.safe_load(fp).get('options', {}) |
3702 | + config_defaults = {key: value.get('default') |
3703 | + for key, value in config_defs.items()} |
3704 | + for opt in config_defs.keys(): |
3705 | + if config.changed(opt): |
3706 | + set_state('config.changed') |
3707 | + set_state('config.changed.{}'.format(opt)) |
3708 | + toggle_state('config.set.{}'.format(opt), config.get(opt)) |
3709 | + toggle_state('config.default.{}'.format(opt), |
3710 | + config.get(opt) == config_defaults[opt]) |
3711 | + hookenv.atexit(clear_config_states) |
3712 | + |
3713 | + |
3714 | +def clear_config_states(): |
3715 | + from charmhelpers.core import hookenv, unitdata |
3716 | + from charms.reactive import remove_state |
3717 | + config = hookenv.config() |
3718 | + remove_state('config.changed') |
3719 | + for opt in config.keys(): |
3720 | + remove_state('config.changed.{}'.format(opt)) |
3721 | + remove_state('config.set.{}'.format(opt)) |
3722 | + remove_state('config.default.{}'.format(opt)) |
3723 | + unitdata.kv().flush() |
3724 | |
3725 | === added file 'lib/charms/layer/execd.py' |
3726 | --- lib/charms/layer/execd.py 1970-01-01 00:00:00 +0000 |
3727 | +++ lib/charms/layer/execd.py 2017-02-15 08:51:47 +0000 |
3728 | @@ -0,0 +1,138 @@ |
3729 | +# Copyright 2014-2016 Canonical Limited. |
3730 | +# |
3731 | +# This file is part of layer-basic, the reactive base layer for Juju. |
3732 | +# |
3733 | +# charm-helpers is free software: you can redistribute it and/or modify |
3734 | +# it under the terms of the GNU Lesser General Public License version 3 as |
3735 | +# published by the Free Software Foundation. |
3736 | +# |
3737 | +# charm-helpers is distributed in the hope that it will be useful, |
3738 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3739 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3740 | +# GNU Lesser General Public License for more details. |
3741 | +# |
3742 | +# You should have received a copy of the GNU Lesser General Public License |
3743 | +# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
3744 | + |
3745 | +# This module may only import from the Python standard library. |
3746 | +import os |
3747 | +import sys |
3748 | +import subprocess |
3749 | +import time |
3750 | + |
3751 | +''' |
3752 | +execd/preinstall |
3753 | + |
3754 | +It is often necessary to configure and reconfigure machines |
3755 | +after provisioning, but before attempting to run the charm. |
3756 | +Common examples are specialized network configuration, enabling |
3757 | +of custom hardware, non-standard disk partitioning and filesystems, |
3758 | +adding secrets and keys required for using a secured network. |
3759 | + |
3760 | +The reactive framework's base layer invokes this mechanism as |
3761 | +early as possible, before any network access is made or dependencies |
3762 | +unpacked or non-standard modules imported (including the charms.reactive |
3763 | +framework itself). |
3764 | + |
3765 | +Operators needing to use this functionality may branch a charm and |
3766 | +create an exec.d directory in it. The exec.d directory in turn contains |
3767 | +one or more subdirectories, each of which contains an executable called |
3768 | +charm-pre-install and any other required resources. The charm-pre-install |
3769 | +executables are run, and if successful, state saved so they will not be |
3770 | +run again. |
3771 | + |
3772 | + $CHARM_DIR/exec.d/mynamespace/charm-pre-install |
3773 | + |
3774 | +An alternative to branching a charm is to compose a new charm that contains |
3775 | +the exec.d directory, using the original charm as a layer, |
3776 | + |
3777 | +A charm author could also abuse this mechanism to modify the charm |
3778 | +environment in unusual ways, but for most purposes it is saner to use |
3779 | +charmhelpers.core.hookenv.atstart(). |
3780 | +''' |
3781 | + |
3782 | + |
3783 | +def default_execd_dir(): |
3784 | + return os.path.join(os.environ['CHARM_DIR'], 'exec.d') |
3785 | + |
3786 | + |
3787 | +def execd_module_paths(execd_dir=None): |
3788 | + """Generate a list of full paths to modules within execd_dir.""" |
3789 | + if not execd_dir: |
3790 | + execd_dir = default_execd_dir() |
3791 | + |
3792 | + if not os.path.exists(execd_dir): |
3793 | + return |
3794 | + |
3795 | + for subpath in os.listdir(execd_dir): |
3796 | + module = os.path.join(execd_dir, subpath) |
3797 | + if os.path.isdir(module): |
3798 | + yield module |
3799 | + |
3800 | + |
3801 | +def execd_submodule_paths(command, execd_dir=None): |
3802 | + """Generate a list of full paths to the specified command within exec_dir. |
3803 | + """ |
3804 | + for module_path in execd_module_paths(execd_dir): |
3805 | + path = os.path.join(module_path, command) |
3806 | + if os.access(path, os.X_OK) and os.path.isfile(path): |
3807 | + yield path |
3808 | + |
3809 | + |
3810 | +def execd_sentinel_path(submodule_path): |
3811 | + module_path = os.path.dirname(submodule_path) |
3812 | + execd_path = os.path.dirname(module_path) |
3813 | + module_name = os.path.basename(module_path) |
3814 | + submodule_name = os.path.basename(submodule_path) |
3815 | + return os.path.join(execd_path, |
3816 | + '.{}_{}.done'.format(module_name, submodule_name)) |
3817 | + |
3818 | + |
3819 | +def execd_run(command, execd_dir=None, stop_on_error=True, stderr=None): |
3820 | + """Run command for each module within execd_dir which defines it.""" |
3821 | + if stderr is None: |
3822 | + stderr = sys.stdout |
3823 | + for submodule_path in execd_submodule_paths(command, execd_dir): |
3824 | + # Only run each execd once. We cannot simply run them in the |
3825 | + # install hook, as potentially storage hooks are run before that. |
3826 | + # We cannot rely on them being idempotent. |
3827 | + sentinel = execd_sentinel_path(submodule_path) |
3828 | + if os.path.exists(sentinel): |
3829 | + continue |
3830 | + |
3831 | + try: |
3832 | + subprocess.check_call([submodule_path], stderr=stderr, |
3833 | + universal_newlines=True) |
3834 | + with open(sentinel, 'w') as f: |
3835 | + f.write('{} ran successfully {}\n'.format(submodule_path, |
3836 | + time.ctime())) |
3837 | + f.write('Removing this file will cause it to be run again\n') |
3838 | + except subprocess.CalledProcessError as e: |
3839 | + # Logs get the details. We can't use juju-log, as the |
3840 | + # output may be substantial and exceed command line |
3841 | + # length limits. |
3842 | + print("ERROR ({}) running {}".format(e.returncode, e.cmd), |
3843 | + file=stderr) |
3844 | + print("STDOUT<<EOM", file=stderr) |
3845 | + print(e.output, file=stderr) |
3846 | + print("EOM", file=stderr) |
3847 | + |
3848 | + # Unit workload status gets a shorter fail message. |
3849 | + short_path = os.path.relpath(submodule_path) |
3850 | + block_msg = "Error ({}) running {}".format(e.returncode, |
3851 | + short_path) |
3852 | + try: |
3853 | + subprocess.check_call(['status-set', 'blocked', block_msg], |
3854 | + universal_newlines=True) |
3855 | + if stop_on_error: |
3856 | + sys.exit(0) # Leave unit in blocked state. |
3857 | + except Exception: |
3858 | + pass # We care about the exec.d/* failure, not status-set. |
3859 | + |
3860 | + if stop_on_error: |
3861 | + sys.exit(e.returncode or 1) # Error state for pre-1.24 Juju |
3862 | + |
3863 | + |
3864 | +def execd_preinstall(execd_dir=None): |
3865 | + """Run charm-pre-install for each module within execd_dir.""" |
3866 | + execd_run('charm-pre-install', execd_dir=execd_dir) |
3867 | |
3868 | === added file 'lib/charms/leadership.py' |
3869 | --- lib/charms/leadership.py 1970-01-01 00:00:00 +0000 |
3870 | +++ lib/charms/leadership.py 2017-02-15 08:51:47 +0000 |
3871 | @@ -0,0 +1,58 @@ |
3872 | +# Copyright 2015-2016 Canonical Ltd. |
3873 | +# |
3874 | +# This file is part of the Leadership Layer for Juju. |
3875 | +# |
3876 | +# This program is free software: you can redistribute it and/or modify |
3877 | +# it under the terms of the GNU General Public License version 3, as |
3878 | +# published by the Free Software Foundation. |
3879 | +# |
3880 | +# This program is distributed in the hope that it will be useful, but |
3881 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
3882 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3883 | +# PURPOSE. See the GNU General Public License for more details. |
3884 | +# |
3885 | +# You should have received a copy of the GNU General Public License |
3886 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3887 | + |
3888 | +from charmhelpers.core import hookenv |
3889 | +from charmhelpers.core import unitdata |
3890 | + |
3891 | +from charms import reactive |
3892 | +from charms.reactive import not_unless |
3893 | + |
3894 | + |
3895 | +__all__ = ['leader_get', 'leader_set'] |
3896 | + |
3897 | + |
3898 | +@not_unless('leadership.is_leader') |
3899 | +def leader_set(settings=None, **kw): |
3900 | + '''Change leadership settings, per charmhelpers.core.hookenv.leader_set. |
3901 | + |
3902 | + The leadership.set.{key} reactive state will be set while the |
3903 | + leadership hook environment setting remains set. |
3904 | + |
3905 | + Changed leadership settings will set the leadership.changed.{key} |
3906 | + and leadership.changed states. These states will remain set until |
3907 | + the following hook. |
3908 | + |
3909 | + These state changes take effect immediately on the leader, and |
3910 | + in future hooks run on non-leaders. In this way both leaders and |
3911 | + non-leaders can share handlers, waiting on these states. |
3912 | + ''' |
3913 | + settings = settings or {} |
3914 | + settings.update(kw) |
3915 | + previous = unitdata.kv().getrange('leadership.settings.', strip=True) |
3916 | + |
3917 | + for key, value in settings.items(): |
3918 | + if value != previous.get(key): |
3919 | + reactive.set_state('leadership.changed.{}'.format(key)) |
3920 | + reactive.set_state('leadership.changed') |
3921 | + reactive.helpers.toggle_state('leadership.set.{}'.format(key), |
3922 | + value is not None) |
3923 | + hookenv.leader_set(settings) |
3924 | + unitdata.kv().update(settings, prefix='leadership.settings.') |
3925 | + |
3926 | + |
3927 | +def leader_get(attribute=None): |
3928 | + '''Return leadership settings, per charmhelpers.core.hookenv.leader_get.''' |
3929 | + return hookenv.leader_get(attribute) |
3930 | |
3931 | === modified file 'metadata.yaml' |
3932 | --- metadata.yaml 2015-12-09 06:50:47 +0000 |
3933 | +++ metadata.yaml 2017-02-15 08:51:47 +0000 |
3934 | @@ -1,14 +1,19 @@ |
3935 | -name: ibm-mq |
3936 | -summary: IBM MQ Messaging product |
3937 | -maintainer: Juju Support <jujusupp@us.ibm.com> |
3938 | -description: | |
3939 | - IBM MQ provides for messaging services to transport multiple types of |
3940 | - data. |
3941 | -tags: |
3942 | - - misc |
3943 | - - messaging |
3944 | - - ibm |
3945 | -subordinate: false |
3946 | -provides: |
3947 | - messaging: |
3948 | - interface: ibm-mq |
3949 | +"name": "ibm-mq" |
3950 | +"summary": "IBM MQ messaging Product" |
3951 | +"maintainer": "IBM Juju Support Team <jujusupp@us.ibm.com>" |
3952 | +"description": | |
3953 | + IBM MQ provides for messaging services to transport multiple types of data. |
3954 | +"tags": |
3955 | +- "ibm" |
3956 | +- "ibm" |
3957 | +- "apt" |
3958 | +- "leadership" |
3959 | +- "mq" |
3960 | +- "misc" |
3961 | +- "ibm" |
3962 | +- "messaging" |
3963 | +"provides": |
3964 | + "messaging": |
3965 | + "interface": "ibm-mq" |
3966 | +"min-juju-version": "2.0-beta1" |
3967 | +"subordinate": !!bool "false" |
3968 | |
3969 | === added directory 'reactive' |
3970 | === added file 'reactive/__init__.py' |
3971 | === added file 'reactive/apt.py' |
3972 | --- reactive/apt.py 1970-01-01 00:00:00 +0000 |
3973 | +++ reactive/apt.py 2017-02-15 08:51:47 +0000 |
3974 | @@ -0,0 +1,131 @@ |
3975 | +# Copyright 2015-2016 Canonical Ltd. |
3976 | +# |
3977 | +# This file is part of the Apt layer for Juju. |
3978 | +# |
3979 | +# This program is free software: you can redistribute it and/or modify |
3980 | +# it under the terms of the GNU General Public License version 3, as |
3981 | +# published by the Free Software Foundation. |
3982 | +# |
3983 | +# This program is distributed in the hope that it will be useful, but |
3984 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
3985 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
3986 | +# PURPOSE. See the GNU General Public License for more details. |
3987 | +# |
3988 | +# You should have received a copy of the GNU General Public License |
3989 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
3990 | + |
3991 | +''' |
3992 | +charms.reactive helpers for dealing with deb packages. |
3993 | + |
3994 | +Add apt package sources using add_source(). Queue deb packages for |
3995 | +installation with install(). Configure and work with your software |
3996 | +once the apt.installed.{packagename} state is set. |
3997 | +''' |
3998 | +import subprocess |
3999 | + |
4000 | +from charmhelpers import fetch |
4001 | +from charmhelpers.core import hookenv |
4002 | +from charmhelpers.core.hookenv import WARNING |
4003 | +from charms import layer |
4004 | +from charms import reactive |
4005 | +from charms.reactive import when, when_not |
4006 | + |
4007 | +import charms.apt |
4008 | + |
4009 | + |
4010 | +@when('apt.needs_update') |
4011 | +def update(): |
4012 | + charms.apt.update() |
4013 | + |
4014 | + |
4015 | +@when('apt.queued_installs') |
4016 | +@when_not('apt.needs_update') |
4017 | +def install_queued(): |
4018 | + charms.apt.install_queued() |
4019 | + |
4020 | + |
4021 | +@when_not('apt.queued_installs') |
4022 | +def ensure_package_status(): |
4023 | + charms.apt.ensure_package_status() |
4024 | + |
4025 | + |
4026 | +def filter_installed_packages(packages): |
4027 | + # Don't use fetch.filter_installed_packages, as it depends on python-apt |
4028 | + # and not available if the basic layer's use_site_packages option is off |
4029 | + # TODO: Move this to charm-helpers.fetch |
4030 | + cmd = ['dpkg-query', '--show', r'--showformat=${Package}\n'] |
4031 | + installed = set(subprocess.check_output(cmd, |
4032 | + universal_newlines=True).split()) |
4033 | + return set(packages) - installed |
4034 | + |
4035 | + |
4036 | +def clear_removed_package_states(): |
4037 | + """On hook startup, clear install states for removed packages.""" |
4038 | + removed = filter_installed_packages(charms.apt.installed()) |
4039 | + if removed: |
4040 | + hookenv.log('{} missing packages ({})'.format(len(removed), |
4041 | + ','.join(removed)), |
4042 | + WARNING) |
4043 | + for package in removed: |
4044 | + reactive.remove_state('apt.installed.{}'.format(package)) |
4045 | + |
4046 | + |
4047 | +def configure_sources(): |
4048 | + """Add user specified package sources from the service configuration. |
4049 | + |
4050 | + See charmhelpers.fetch.configure_sources for details. |
4051 | + """ |
4052 | + config = hookenv.config() |
4053 | + |
4054 | + # We don't have enums, so we need to validate this ourselves. |
4055 | + package_status = config.get('package_status') |
4056 | + if package_status not in ('hold', 'install'): |
4057 | + charms.apt.status_set('blocked', |
4058 | + 'Unknown package_status {}' |
4059 | + ''.format(package_status)) |
4060 | + # Die before further hooks are run. This isn't very nice, but |
4061 | + # there is no other way to inform the operator that they have |
4062 | + # invalid configuration. |
4063 | + raise SystemExit(0) |
4064 | + |
4065 | + sources = config.get('install_sources') |
4066 | + keys = config.get('install_keys') |
4067 | + if reactive.helpers.data_changed('apt.configure_sources', (sources, keys)): |
4068 | + fetch.configure_sources(update=False, |
4069 | + sources_var='install_sources', |
4070 | + keys_var='install_keys') |
4071 | + reactive.set_state('apt.needs_update') |
4072 | + |
4073 | + extra_packages = sorted(config.get('extra_packages', '').split()) |
4074 | + if extra_packages: |
4075 | + charms.apt.queue_install(extra_packages) |
4076 | + |
4077 | + |
4078 | +def queue_layer_packages(): |
4079 | + """Add packages listed in build-time layer options.""" |
4080 | + # Both basic and apt layer. basic layer will have already installed |
4081 | + # its defined packages, but rescheduling it here gets the apt layer |
4082 | + # state set and they will pinned as any other apt layer installed |
4083 | + # package. |
4084 | + opts = layer.options() |
4085 | + for section in ['basic', 'apt']: |
4086 | + if section in opts and 'packages' in opts[section]: |
4087 | + charms.apt.queue_install(opts[section]['packages']) |
4088 | + |
4089 | + |
4090 | +# Per https://github.com/juju-solutions/charms.reactive/issues/33, |
4091 | +# this module may be imported multiple times so ensure the |
4092 | +# initialization hook is only registered once. I have to piggy back |
4093 | +# onto the namespace of a module imported before reactive discovery |
4094 | +# to do this. |
4095 | +if not hasattr(reactive, '_apt_registered'): |
4096 | + # We need to register this to run every hook, not just during install |
4097 | + # and config-changed, to protect against race conditions. If we don't |
4098 | + # do this, then the config in the hook environment may show updates |
4099 | + # to running hooks well before the config-changed hook has been invoked |
4100 | + # and the intialization provided an opertunity to be run. |
4101 | + hookenv.atstart(hookenv.log, 'Initializing Apt Layer') |
4102 | + hookenv.atstart(clear_removed_package_states) |
4103 | + hookenv.atstart(configure_sources) |
4104 | + hookenv.atstart(queue_layer_packages) |
4105 | + reactive._apt_registered = True |
4106 | |
4107 | === added file 'reactive/ibm-base.sh' |
4108 | --- reactive/ibm-base.sh 1970-01-01 00:00:00 +0000 |
4109 | +++ reactive/ibm-base.sh 2017-02-15 08:51:47 +0000 |
4110 | @@ -0,0 +1,107 @@ |
4111 | +#!/bin/bash |
4112 | +source charms.reactive.sh |
4113 | +set -e |
4114 | + |
4115 | + |
4116 | +# Utility function to verify a downloaded resource |
4117 | +# :param: file name |
4118 | +# :param: checksum type |
4119 | +# :param: checksum value |
4120 | +function verify_curl_resource() { |
4121 | + local FILE=$1 |
4122 | + local TYPE=$2 |
4123 | + local EXPECTED_SUM=$3 |
4124 | + local CALCULATED_SUM="" |
4125 | + local PROG="" |
4126 | + |
4127 | + if [ ! -r ${FILE} ]; then |
4128 | + status-set blocked "ibm-base: could not read ${FILE}" |
4129 | + juju-log "Could not verify the downloaded resource. File could not be read: ${FILE}" |
4130 | + fi |
4131 | + |
4132 | + # Set our checksum utility based on the requested type |
4133 | + case "${TYPE}" in |
4134 | + md5) |
4135 | + PROG='md5sum' |
4136 | + ;; |
4137 | + sha256) |
4138 | + PROG='sha256sum' |
4139 | + ;; |
4140 | + sha512) |
4141 | + PROG='sha512sum' |
4142 | + ;; |
4143 | + *) |
4144 | + status-set blocked "ibm-base: checksum type must be md5, sha215, or sha512" |
4145 | + juju-log "Could not verify the downloaded resource ${FILE}. Unknown checksum type: ${TYPE}" |
4146 | + return 1 |
4147 | + esac |
4148 | + |
4149 | + CALCULATED_SUM=`${PROG} ${FILE} | awk '{print $1}'` |
4150 | + if [ "${CALCULATED_SUM}" = "${EXPECTED_SUM}" ]; then |
4151 | + juju-log "Checksum verified for ${FILE}." |
4152 | + return 0 |
4153 | + else |
4154 | + status-set blocked "ibm-base: checksums did not match" |
4155 | + juju-log "Checksum mismatch for ${FILE}. Expected ${EXPECTED_SUM}, got ${CALCULATED_SUM}" |
4156 | + return 1 |
4157 | + fi |
4158 | +} |
4159 | + |
4160 | + |
4161 | +# Fetch curl resources if a URL is configured |
4162 | +@when 'config.set.curl_url' |
4163 | +@when_any 'config.new.curl_url' 'config.changed.curl_url' 'config.new.curl_opts' 'config.changed.curl_opts' |
4164 | +function fetch_curl_resource() { |
4165 | + local ARCHIVE_DIR="${CHARM_DIR}/files/archives" |
4166 | + local CURL_URL=$(config-get 'curl_url') |
4167 | + local CURL_OPTS=$(config-get 'curl_opts') |
4168 | + |
4169 | + status-set maintenance "ibm-base: fetching resource(s)" |
4170 | + |
4171 | + mkdir -p ${ARCHIVE_DIR} |
4172 | + cd ${ARCHIVE_DIR} |
4173 | + # Multiple URLs may be separated by a space, so loop. |
4174 | + for URL_STRING in ${CURL_URL} |
4175 | + do |
4176 | + # For each URL_STRING, set the url, checksum type, and checksum value. |
4177 | + local URL=${URL_STRING%%\?*} # string before the first '?' |
4178 | + local FILE_NAME=${URL##*\/} # string after the last '/' |
4179 | + local SUM_STRING=${URL_STRING#*\?} # string after the first '?' |
4180 | + local SUM_TYPE=${SUM_STRING%%\=*} # string before the first '=' |
4181 | + local SUM_VALUE=${SUM_STRING#*\=} # string after the first '=' |
4182 | + |
4183 | + if [ -z ${FILE_NAME} ]; then |
4184 | + FILE_NAME="juju-${RANDOM}" |
4185 | + fi |
4186 | + curl --silent --show-error ${CURL_OPTS} -o ${FILE_NAME} ${URL} |
4187 | + |
4188 | + # Verify our resource checksum. If this fails, let verify_resource log |
4189 | + # the reason and exit successfully. Exiting non-zero would fail the hook, |
4190 | + # so return 0 and simply inform the user that verification failed. |
4191 | + verify_curl_resource ${FILE_NAME} ${SUM_TYPE} ${SUM_VALUE} || return 0 |
4192 | + done |
4193 | + cd - |
4194 | + |
4195 | + set_state 'ibm-base.curl.resource.fetched' |
4196 | + status-set active "ibm-base: curl resource(s) fetched" |
4197 | + juju-log 'Curl resource fetched' |
4198 | +} |
4199 | + |
4200 | + |
4201 | +# Handle license acceptance |
4202 | +@when 'config.changed.license_accepted' |
4203 | +function check_license_acceptance() { |
4204 | + local LIC_ACCEPTED=$(config-get 'license_accepted') |
4205 | + |
4206 | + # compare lowercase LIC_ACCEPTED (requires bash > 4) |
4207 | + if [ "${LIC_ACCEPTED,,}" = "true" ]; then |
4208 | + set_state 'ibm-base.license.accepted' |
4209 | + juju-log 'License accepted' |
4210 | + else |
4211 | + remove_state 'ibm-base.license.accepted' |
4212 | + juju-log 'License NOT accepted' |
4213 | + fi |
4214 | +} |
4215 | + |
4216 | +# Main reactive entry point |
4217 | +reactive_handler_main |
4218 | |
4219 | === added file 'reactive/ibm-mq.sh' |
4220 | --- reactive/ibm-mq.sh 1970-01-01 00:00:00 +0000 |
4221 | +++ reactive/ibm-mq.sh 2017-02-15 08:51:47 +0000 |
4222 | @@ -0,0 +1,340 @@ |
4223 | +#!/bin/bash |
4224 | + |
4225 | +set -ex |
4226 | +source charms.reactive.sh |
4227 | + |
4228 | +ARCHIVE_DIR=$CHARM_DIR/files/archives |
4229 | +MQ_INSTALL_PATH=/opt/mqm |
4230 | +ARCHITECTURE=`uname -m` |
4231 | +RPM_INSTALL_ARG="-ivh --nodeps --force-debian" |
4232 | +relatedService="" |
4233 | +QMA="" |
4234 | +QM_Name="" |
4235 | +hostname=`unit-get private-address` |
4236 | + if [ "$ARCHITECTURE" = "ppc64le" ]; then |
4237 | + RPM_INSTALL_ARG="-ivh --nodeps --force-debian --ignorearch" |
4238 | + fi |
4239 | + |
4240 | + |
4241 | +# Check whether IBM MQ is installed |
4242 | + |
4243 | +is_mq_installed() |
4244 | +{ |
4245 | + if [ -d $MQ_INSTALL_PATH/bin ]; then |
4246 | + source $MQ_INSTALL_PATH/bin/setmqenv -s |
4247 | + if [ $? == 0 ]; then |
4248 | + source $MQ_INSTALL_PATH/bin/setmqenv -s |
4249 | + echo "True" |
4250 | + fi |
4251 | + else |
4252 | + echo "False" |
4253 | + fi |
4254 | +} |
4255 | + |
4256 | +# Remove MQ, if installed |
4257 | +remove_software() |
4258 | +{ |
4259 | + |
4260 | + mq_installed=`is_mq_installed` |
4261 | + if [ $mq_installed == True ]; then |
4262 | + juju-log "Removing IBM MQ software." |
4263 | + # Stop all queues |
4264 | + questr="QMNAME(" |
4265 | + for queue in `$MQ_INSTALL_PATH/bin/dspmq -o installation | cut -d' ' -f1 `; |
4266 | + do |
4267 | + # Get the queue manager name |
4268 | + queue_mgr_name=${queue:${#questr}:${#queue}-${#questr}-1} |
4269 | + juju-log "Stopping queue manager $queue_mgr_name" |
4270 | + set +e |
4271 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqm -w $queue_mgr_name" |
4272 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqlsr -w -m $queue_mgr_name" |
4273 | + set -e |
4274 | + done |
4275 | + # Get list of packages and remove them |
4276 | + mq_rpms="`rpm -qa | grep MQSeries`" |
4277 | + juju-log "Removing package(s) $mq_rpms" |
4278 | + rpm -ev --force-debian $mq_rpms |
4279 | + fi |
4280 | + |
4281 | +} |
4282 | + |
4283 | +# Update system configuration after installing MQ |
4284 | +configure_system() |
4285 | +{ |
4286 | + juju-log "IBM MQ: Updating system configuration." |
4287 | + # Some containers do not allow system updates. |
4288 | + # Prevent the script from failing in such cases |
4289 | + set +e |
4290 | + |
4291 | + # Add user ubuntu to mqm group |
4292 | + adduser ubuntu mqm |
4293 | + |
4294 | + # Update mqm file limits |
4295 | + |
4296 | + hard_nf=`su - mqm -c "ulimit -Hn"` |
4297 | + soft_nf=`su - mqm -c "ulimit -Sn"` |
4298 | + |
4299 | + if [ $hard_nf -lt 10240 -o $soft_nf -lt 10240 ]; then |
4300 | + if [ "`grep mqm /etc/security/limits.conf`"=="" ]; then |
4301 | + sed -i 's/# End of file/mqm hard nofile 10240\nmqm soft nofile 10240\n# End of file/' /etc/security/limits.conf |
4302 | + fi |
4303 | + #Update /etc/pam.d/common-session file to take effect the above changes |
4304 | + result_search=`grep 'session required pam_limits.so' /etc/pam.d/common-session` |
4305 | + if [ -z "$result_search" ]; then |
4306 | + sed -i 's/# end of pam-auth-update config/session required pam_limits.so\n# end of pam-auth-update config/' /etc/pam.d/common-session |
4307 | + fi |
4308 | + fi |
4309 | + |
4310 | + set -e |
4311 | + juju-log "IBM MQ: Completed system configuration update." |
4312 | + |
4313 | +} |
4314 | + |
4315 | +# create Queue manager and Queue |
4316 | +create_QM_Queue() |
4317 | +{ |
4318 | + |
4319 | + juju-log "IBM MQ: Verifying installation." |
4320 | + |
4321 | + relatedService=$1 |
4322 | + consumer_hostname=$2 |
4323 | + |
4324 | + QMA="${relatedService//-}.queue.manager" |
4325 | + |
4326 | + # Prevent the script from failing on failure. |
4327 | + # It could because configure_system call failed |
4328 | + set +e |
4329 | + |
4330 | + # Run as mqm user as root will not work |
4331 | + |
4332 | + # Create queue manager |
4333 | + juju-log "IBM MQ: Create queue manager $QMA." |
4334 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/crtmqm $QMA" |
4335 | + rc=$? |
4336 | + if [ $rc == 0 ]; then |
4337 | + juju-log "IBM MQ: queue manager $QMA created." |
4338 | + QM_create=1 |
4339 | + elif [ $rc == 8 ]; then |
4340 | + QM_exists=1 |
4341 | + else |
4342 | + juju-log "IBM MQ: Failed to create queue manager $QMA." |
4343 | + fi |
4344 | + |
4345 | + # Start queue manager |
4346 | + if [ $QM_create -eq 1 ]; then |
4347 | + juju-log "IBM MQ: Starting queue manager $QMA." |
4348 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/strmqm $QMA" |
4349 | + if [ $? == 0 ]; then |
4350 | + juju-log "IBM MQ: Queue manager $QMA started." |
4351 | + else |
4352 | + juju-log "IBM MQ: Failed to start queue manager $QMA." |
4353 | + |
4354 | + fi |
4355 | + |
4356 | + # set the port to listen queue manager |
4357 | + new_port=1414 |
4358 | + free_port=0 |
4359 | + is_free=0 |
4360 | + |
4361 | + #Check whether the port number is already in use |
4362 | + while [ $free_port -eq 0 ] |
4363 | + do |
4364 | + |
4365 | + #is_free=`netstat -lnp | grep $new_port | cut -d":" -f2 | cut -d" " -f1` |
4366 | + is_free=`netstat -an | grep $new_port | grep LISTEN` |
4367 | + if [ -z "$is_free" ]; then |
4368 | + free_port=1 |
4369 | + else |
4370 | + new_port=$((new_port+1)) |
4371 | + fi |
4372 | + |
4373 | + done |
4374 | + |
4375 | + juju-log "IBM MQ: Setting port to $new_port" |
4376 | + |
4377 | + #start queue manager in listener mode |
4378 | + juju-log "IBM MQ: Starting queue manager $QMA in listener mode" |
4379 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/runmqlsr -m $QMA -t tcp -p $new_port" & |
4380 | + if [ $? == 0 ]; then |
4381 | + juju-log "IBM MQ: Queue manager $QMA listening on $new_port" |
4382 | + else |
4383 | + juju-log "IBM MQ: Queue manager $QMA failed to listen" |
4384 | + fi |
4385 | + |
4386 | + # Create queue and setup the queue configuration for consumer service |
4387 | + juju-log "IBM MQ: Creating queue." |
4388 | + |
4389 | + sudo bash -c "cat > $CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc << EOF |
4390 | +DEFINE QLOCAL (QUEUE1) |
4391 | +DEFINE CHANNEL ('MDB.SVRCONN') CHLTYPE(SVRCONN) TRPTYPE(TCP) |
4392 | +SET CHLAUTH ('MDB.SVRCONN') TYPE(ADDRESSMAP) ADDRESS($consumer_hostname) MCAUSER('mqm') |
4393 | +SET AUTHREC OBJTYPE(QMGR) PRINCIPAL('mqm') AUTHADD(CONNECT,INQ) |
4394 | +SET AUTHREC PROFILE('QUEUE1') OBJTYPE(QUEUE) PRINCIPAL('mqm') AUTHADD(PUT,GET,INQ,BROWSE) |
4395 | +SET AUTHREC PROFILE('SYSTEM.BASE.TOPIC') OBJTYPE(TOPIC) PRINCIPAL('mqm') AUTHADD(PUB, SUB) |
4396 | +EOF" |
4397 | + if [ $? == 0 ]; then |
4398 | + # Create file to delete the above queue when relation breaks |
4399 | + sudo bash -c "echo 'DELETE QLOCAL(QUEUE1) PURGE' > $CHARM_DIR/files/archives/${relatedService//-}_mq_delete_queue.mqsc" |
4400 | + fi |
4401 | + |
4402 | + if [ -f $CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc ]; then |
4403 | + |
4404 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/runmqsc $QMA < $CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc" |
4405 | + if [ $? == 0 ]; then |
4406 | + juju-log "IBM MQ: Queue created." |
4407 | + else |
4408 | + juju-log "IBM MQ: Failed to create queue." |
4409 | + fi |
4410 | + |
4411 | + QM_Name=$QMA |
4412 | + Qname=$CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc |
4413 | + port=$new_port |
4414 | + open-port $port/TCP |
4415 | + |
4416 | + juju-log "create_QM_Queue values are QM_name=$QM_Name Qname=$Qname hostname=$hostname port=$port" |
4417 | + fi |
4418 | + elif [ $QM_exists -eq 1 ];then |
4419 | + QM_Name=$QMA |
4420 | + Qname=$CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc |
4421 | + port=`ps -ef| grep runmqlsr | grep "$QMA" | grep -v grep | grep "su -l mqm" | awk -F"-p " '{print $2}'` |
4422 | + juju-log "relation with ${relatedService} already exists" |
4423 | + juju-log "create_QM_Queue values are QM_name=$QM_Name Qname=$Qname hostname=$hostname port=$port" |
4424 | + fi |
4425 | + |
4426 | +} |
4427 | + |
4428 | + |
4429 | +# IBM MQ: Deleting queue |
4430 | +delete_QM_Queue() |
4431 | +{ |
4432 | + |
4433 | + relatedService=$1 |
4434 | + |
4435 | + QMA="${relatedService//-}.queue.manager" |
4436 | + # Delete queue |
4437 | + juju-log "IBM MQ: Deleting queue." |
4438 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/runmqsc $QMA < $CHARM_DIR/files/archives/${relatedService//-}_mq_delete_queue.mqsc" |
4439 | + if [ $? == 0 ]; then |
4440 | + juju-log "IBM MQ: Deleted queue." |
4441 | + else |
4442 | + juju-log "IBM MQ: Failed to delete queue." |
4443 | + fi |
4444 | + |
4445 | + |
4446 | + port=`ps -ef| grep runmqlsr | grep "$QMA" | grep -v grep | grep "su -l mqm" | awk -F"-p " '{print $2}'` |
4447 | + |
4448 | + juju-log "IBM MQ: stop Queue manager from listening on port" |
4449 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqlsr -m $QMA -w" |
4450 | + if [ $? == 0 ]; then |
4451 | + juju-log "IBM MQ: Stopped queue manager from listening." |
4452 | + else |
4453 | + juju-log "IBM MQ: Failed to stop queue manager to listen." |
4454 | + fi |
4455 | + |
4456 | + #close-port the open-port for the consumer |
4457 | + close-port $port |
4458 | + |
4459 | + # End queue manger |
4460 | + juju-log "IBM MQ: Stopping queue manager." |
4461 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqm $QMA" |
4462 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/endmqlsr -w -m $QMA" |
4463 | + if [ $? -eq 0 ]; then |
4464 | + juju-log "IBM MQ: Queue Manager is stopped." |
4465 | + else |
4466 | + juju-log "IBM MQ: Queue Manager failed to stop." |
4467 | + fi |
4468 | + |
4469 | + sleep 10 |
4470 | + |
4471 | + juju-log "IBM MQ: Deleting queue manager." |
4472 | + su -l mqm -c "$MQ_INSTALL_PATH/bin/dltmqm $QMA" |
4473 | + if [ $? -eq 0 ]; then |
4474 | + juju-log "IBM MQ: Queue Manager is deleted." |
4475 | + else |
4476 | + juju-log "IBM MQ: Queue Manager could not be deleted." |
4477 | + fi |
4478 | + |
4479 | + #clean up the created MQSC files |
4480 | + if [ -f $CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc ];then |
4481 | + sudo bash -c "rm $CHARM_DIR/files/archives/${relatedService//-}_mq_create_queue.mqsc" |
4482 | + fi |
4483 | + if [ -f $CHARM_DIR/files/archives/${relatedService//-}_mq_delete_queue.mqsc ]; then |
4484 | + sudo bash -c "rm $CHARM_DIR/files/archives/${relatedService//-}_mq_delete_queue.mqsc" |
4485 | + fi |
4486 | + |
4487 | + set -e |
4488 | + juju-log "IBM MQ: Deleting queue completed." |
4489 | +} |
4490 | + |
4491 | +@when 'ibm-base.license.accepted' 'ibm-base.curl.resource.fetched' |
4492 | +@when_not 'ibm-mq.installed' |
4493 | +function mq_install() { |
4494 | + juju-log "inside mq install function." |
4495 | + mq_inst=`is_mq_installed` |
4496 | + juju-log "mq_inst==$mq_inst" |
4497 | + if [ -f $CHARM_DIR/files/archives/*.gz ]; then |
4498 | + juju-log "IBM MQ: Extracting IBM MQ package." |
4499 | + tar xvfz $CHARM_DIR/files/archives/*.gz --strip-components=1 -C $CHARM_DIR/files/archives |
4500 | + rm -rf $CHARM_DIR/files/archives/*.gz |
4501 | + juju-log "IBM MQ: Extracted IBM MQ package." |
4502 | + juju-log "Awaiting acceptance of IBM MQ license (see README on how to accept the license)." |
4503 | + fi |
4504 | + if [ $mq_inst == False ]; then |
4505 | + # Check MQ package availability |
4506 | + if [ -f $CHARM_DIR/files/archives/MQSeriesServer*rpm ] && [ -f $CHARM_DIR/files/archives/MQSeriesRuntime*rpm ]; |
4507 | + then |
4508 | + echo "MQ Packages available for installation."; |
4509 | + $CHARM_DIR/files/archives/mqlicense.sh -accept |
4510 | + juju-log "Installing available MQ packages." |
4511 | + rpm $RPM_INSTALL_ARG --prefix $MQ_INSTALL_PATH $CHARM_DIR/files/archives/MQSeries*rpm |
4512 | + juju-log "Installation of available MQ packages complete." |
4513 | + # Configure system values for MQ |
4514 | + configure_system |
4515 | + set_state 'ibm-mq.installed' |
4516 | + status-set active "MQ installed successfully" |
4517 | + else |
4518 | + status-set blocked "MQ Packages missing. Please check README file Upgrade MQ charm after adding the MQ packages" |
4519 | + fi |
4520 | + fi |
4521 | +} |
4522 | + |
4523 | +@when 'ibm-mq.installed' |
4524 | +@when 'messaging.connected' |
4525 | +function export_mq_details(){ |
4526 | + juju-log "on ibm_mq.connected triggered here" |
4527 | + services=$(relation_call --state=messaging.connected services) || true |
4528 | + for service in $services; do |
4529 | + juju-log "$service" |
4530 | + consumer_hostname=$(relation_call --state=messaging.connected get_consumer_hostname) || true |
4531 | + create_QM_Queue $service $consumer_hostname |
4532 | + juju-log "create_QM_Queue values are QM_name=$QM_Name Qname=$Qname hostname=$hostname port=$port" |
4533 | + relation_call --state=messaging.connected set_mq_details $service $QM_Name $Qname $hostname $port || true |
4534 | + done |
4535 | +} |
4536 | + |
4537 | +@when 'messaging.departed' 'ibm-mq.installed' |
4538 | +function remove_mq_QM_Q() { |
4539 | + juju-log "in delete_QM_Queue.." |
4540 | + services=$(relation_call --state=messaging.departed services) || true |
4541 | + for service in $services; do |
4542 | + juju-log "$service" |
4543 | + delete_QM_Queue $service |
4544 | + juju-log "delete_QM_Queue done." |
4545 | + juju-log "Calling dismiss for $service" |
4546 | + relation_call --state=messaging.departed dismiss $service || true |
4547 | + done |
4548 | + } |
4549 | + |
4550 | +@when 'ibm-mq.installed' |
4551 | +@when_not 'ibm-base.license.accepted' |
4552 | +function uninstall() { |
4553 | + # Uninstall all versions of IBM MQ |
4554 | + status-set maintenance "Uninstalling IBM MQ" |
4555 | + mq_license_accepted=`config-get accept-ibm-mq-license` |
4556 | + remove_software |
4557 | + remove_state 'ibm-mq.installed' |
4558 | + remove_state 'messaging.connected' |
4559 | + status-set blocked "IBM MQ Uninstalled" |
4560 | +} |
4561 | + |
4562 | +reactive_handler_main |
4563 | |
4564 | === added file 'reactive/leadership.py' |
4565 | --- reactive/leadership.py 1970-01-01 00:00:00 +0000 |
4566 | +++ reactive/leadership.py 2017-02-15 08:51:47 +0000 |
4567 | @@ -0,0 +1,68 @@ |
4568 | +# Copyright 2015-2016 Canonical Ltd. |
4569 | +# |
4570 | +# This file is part of the Leadership Layer for Juju. |
4571 | +# |
4572 | +# This program is free software: you can redistribute it and/or modify |
4573 | +# it under the terms of the GNU General Public License version 3, as |
4574 | +# published by the Free Software Foundation. |
4575 | +# |
4576 | +# This program is distributed in the hope that it will be useful, but |
4577 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
4578 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
4579 | +# PURPOSE. See the GNU General Public License for more details. |
4580 | +# |
4581 | +# You should have received a copy of the GNU General Public License |
4582 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
4583 | + |
4584 | +from charmhelpers.core import hookenv |
4585 | +from charmhelpers.core import unitdata |
4586 | + |
4587 | +from charms import reactive |
4588 | +from charms.leadership import leader_get, leader_set |
4589 | + |
4590 | + |
4591 | +__all__ = ['leader_get', 'leader_set'] # Backwards compatibility |
4592 | + |
4593 | + |
4594 | +def initialize_leadership_state(): |
4595 | + '''Initialize leadership.* states from the hook environment. |
4596 | + |
4597 | + Invoked by hookenv.atstart() so states are available in |
4598 | + @hook decorated handlers. |
4599 | + ''' |
4600 | + is_leader = hookenv.is_leader() |
4601 | + if is_leader: |
4602 | + hookenv.log('Initializing Leadership Layer (is leader)') |
4603 | + else: |
4604 | + hookenv.log('Initializing Leadership Layer (is follower)') |
4605 | + |
4606 | + reactive.helpers.toggle_state('leadership.is_leader', is_leader) |
4607 | + |
4608 | + previous = unitdata.kv().getrange('leadership.settings.', strip=True) |
4609 | + current = hookenv.leader_get() |
4610 | + |
4611 | + # Handle deletions. |
4612 | + for key in set(previous.keys()) - set(current.keys()): |
4613 | + current[key] = None |
4614 | + |
4615 | + any_changed = False |
4616 | + for key, value in current.items(): |
4617 | + reactive.helpers.toggle_state('leadership.changed.{}'.format(key), |
4618 | + value != previous.get(key)) |
4619 | + if value != previous.get(key): |
4620 | + any_changed = True |
4621 | + reactive.helpers.toggle_state('leadership.set.{}'.format(key), |
4622 | + value is not None) |
4623 | + reactive.helpers.toggle_state('leadership.changed', any_changed) |
4624 | + |
4625 | + unitdata.kv().update(current, prefix='leadership.settings.') |
4626 | + |
4627 | + |
4628 | +# Per https://github.com/juju-solutions/charms.reactive/issues/33, |
4629 | +# this module may be imported multiple times so ensure the |
4630 | +# initialization hook is only registered once. I have to piggy back |
4631 | +# onto the namespace of a module imported before reactive discovery |
4632 | +# to do this. |
4633 | +if not hasattr(reactive, '_leadership_registered'): |
4634 | + hookenv.atstart(initialize_leadership_state) |
4635 | + reactive._leadership_registered = True |
4636 | |
4637 | === added file 'reactive/test' |
4638 | --- reactive/test 1970-01-01 00:00:00 +0000 |
4639 | +++ reactive/test 2017-02-15 08:51:47 +0000 |
4640 | @@ -0,0 +1,1 @@ |
4641 | +DEFINE QLOCAL (QUEUE1) |
4642 | |
4643 | === added file 'requirements.txt' |
4644 | --- requirements.txt 1970-01-01 00:00:00 +0000 |
4645 | +++ requirements.txt 2017-02-15 08:51:47 +0000 |
4646 | @@ -0,0 +1,2 @@ |
4647 | +flake8 |
4648 | +pytest |
4649 | |
4650 | === modified file 'revision' |
4651 | --- revision 2015-03-11 19:52:30 +0000 |
4652 | +++ revision 2017-02-15 08:51:47 +0000 |
4653 | @@ -1,1 +1,1 @@ |
4654 | -1 |
4655 | +1 |
4656 | \ No newline at end of file |
4657 | |
4658 | === modified file 'tests/00-setup' |
4659 | --- tests/00-setup 2015-06-10 10:41:46 +0000 |
4660 | +++ tests/00-setup 2017-02-15 08:51:47 +0000 |
4661 | @@ -1,5 +1,17 @@ |
4662 | #!/bin/bash |
4663 | +MQ_CURL_URL=${MQ_CURL_URL?Error: IBM MQ curl_url be defined in tests/00-setup} |
4664 | +MQ_CURL_OPTS=${MQ_CURL_OPTS?Error: IBM MQ curl_OPTS be defined in tests/00-setup} |
4665 | +MQ_LICENSE=${MQ_LICENSE?Error: IBM MQ License accepted value must be defined in tests/00-setup} |
4666 | + |
4667 | +# Add a local configuration file |
4668 | +cat << EOF > local.yaml |
4669 | +mq: |
4670 | + mq_curl_url: "$MQ_CURL_URL" |
4671 | + mq_curl_opts: "$MQ_CURL_OPTS" |
4672 | + mq_license_accepted: "$MQ_LICENSE" |
4673 | +EOF |
4674 | |
4675 | sudo add-apt-repository ppa:juju/stable -y |
4676 | sudo apt-get update |
4677 | -sudo apt-get install amulet -y |
4678 | +sudo apt-get -y install unzip |
4679 | +sudo apt-get install amulet python3 -y |
4680 | |
4681 | === modified file 'tests/10-bundles-test.py' |
4682 | --- tests/10-bundles-test.py 2015-07-15 08:13:51 +0000 |
4683 | +++ tests/10-bundles-test.py 2017-02-15 08:51:47 +0000 |
4684 | @@ -1,11 +1,11 @@ |
4685 | #!/usr/bin/env python3 |
4686 | - |
4687 | # This amulet test deploys the bundles.yaml file in this directory. |
4688 | |
4689 | import os |
4690 | import unittest |
4691 | import yaml |
4692 | import amulet |
4693 | +import sys |
4694 | |
4695 | # Lots of prereqs on this charm (eg: java), so give it a large timeout |
4696 | seconds_to_wait = 1500 |
4697 | @@ -16,14 +16,41 @@ |
4698 | @classmethod |
4699 | def setUpClass(cls): |
4700 | """ Set up an amulet deployment using the bundle. """ |
4701 | - d = amulet.Deployment() |
4702 | - bundle_path = os.path.join(os.path.dirname(__file__), 'bundles.yaml') |
4703 | - with open(bundle_path, 'r') as bundle_file: |
4704 | - contents = yaml.safe_load(bundle_file) |
4705 | - d.load(contents) |
4706 | + d = amulet.Deployment(juju_env='local', series='trusty') |
4707 | + local_path = os.path.join(os.path.dirname(__file__), 'local.yaml') |
4708 | + with open(local_path, "r") as fd: |
4709 | + config = yaml.safe_load(fd) |
4710 | + curl_url = config.get('mq').get('mq_curl_url') |
4711 | + print('Using mq_curl_url %s' % curl_url) |
4712 | + # Test if a IBM mq curl url is defined |
4713 | + if not curl_url: |
4714 | + print("You need to define a im_curl_url.\n" |
4715 | + "Edit local.yaml or tests/00-setup and run it again.") |
4716 | + sys.exit(1) |
4717 | + |
4718 | + curl_opts = config.get('mq').get('mq_curl_opts') |
4719 | + print('Using mq_curl_opts %s' % curl_opts) |
4720 | + # Test if IBM curl_opts is defined |
4721 | + if not curl_opts: |
4722 | + print("You need to define a curl_opts." |
4723 | + " repository.\n Edit local.yaml or tests/00-setup" |
4724 | + " and run it again.") |
4725 | + sys.exit(1) |
4726 | + license_accepted = config.get('mq').get('mq_license_accepted') |
4727 | + print('Using license_accepted %s' % license_accepted) |
4728 | + # Test if a license_accepted is defined |
4729 | + if not license_accepted: |
4730 | + print("You need to define a license_accepted for the IBM MQ" |
4731 | + " product repository.\n Edit local.yaml or tests/00-setup" |
4732 | + " and run it again.") |
4733 | + sys.exit(1) |
4734 | + |
4735 | + d.add('ibm-mq') |
4736 | |
4737 | # Software doesn't actually install until you accept the license |
4738 | - d.configure('ibm-mq', {'accept-ibm-mq-license': True}) |
4739 | + d.configure('ibm-mq', {'license_accepted': license_accepted, |
4740 | + 'curl_url': curl_url, |
4741 | + 'curl_opts': curl_opts}) |
4742 | |
4743 | d.setup(seconds_to_wait) |
4744 | d.sentry.wait(seconds_to_wait) |
4745 | @@ -34,22 +61,26 @@ |
4746 | self.assertTrue(self.d.deployed) |
4747 | |
4748 | def test_running(self): |
4749 | - """ Test that, if deployed, everything is set up and running correctly. """ |
4750 | - ibm_mq_unit = self.d.sentry.unit['ibm-mq/0'] |
4751 | + """ Test that, if deployed, |
4752 | + everything is set up and running correctly. """ |
4753 | + ibm_mq_unit = self.d.sentry.unit['ibm-mq'][0] |
4754 | |
4755 | # Configure system values |
4756 | output, code = ibm_mq_unit.run('sysctl kernel.shmmax | cut -d= -f2') |
4757 | print(output) |
4758 | if code != 0 or int(output) < 268435456: |
4759 | - # this isn't really a pass/fail test, as it depends on the environment |
4760 | - # to which the charm is deployed, and doesn't block the install |
4761 | - print('Unable to set kernel.shmmax value (perhaps on local provider?).') |
4762 | + # this isn't really a pass/fail test, as it depends |
4763 | + # on the environment to which |
4764 | + # the charm is deployed, and doesn't block the install |
4765 | + print('Unable to set kernel.shmmax' |
4766 | + ' value (perhaps on local provider?).') |
4767 | |
4768 | output, code = ibm_mq_unit.run('sysctl fs.file-max | cut -d= -f2') |
4769 | print(output) |
4770 | if code != 0 or int(output) < 524288: |
4771 | - # this isn't really a pass/fail test, as it depends on the environment |
4772 | - # to which the charm is deployed, and doesn't block the install |
4773 | + # this isn't really a pass/fail test, as it depends |
4774 | + # on the environment to which |
4775 | + # the charm is deployed, and doesn't block the install |
4776 | print('Unable to set fs.file value (perhaps on local provider?).') |
4777 | |
4778 | output, code = ibm_mq_unit.run('sysctl -p') |
4779 | |
4780 | === removed file 'tests/bundles.yaml' |
4781 | --- tests/bundles.yaml 2015-07-09 18:38:01 +0000 |
4782 | +++ tests/bundles.yaml 1970-01-01 00:00:00 +0000 |
4783 | @@ -1,10 +0,0 @@ |
4784 | -ibm-mq-liberty-bundle: |
4785 | - services: |
4786 | - "ibm-mq": |
4787 | - charm: "ibm-mq" |
4788 | - num_units: 1 |
4789 | - annotations: |
4790 | - "gui-x": "300" |
4791 | - "gui-y": "300" |
4792 | - relations: [] |
4793 | - series: trusty |
4794 | |
4795 | === added file 'tox.ini' |
4796 | --- tox.ini 1970-01-01 00:00:00 +0000 |
4797 | +++ tox.ini 2017-02-15 08:51:47 +0000 |
4798 | @@ -0,0 +1,12 @@ |
4799 | +[tox] |
4800 | +skipsdist=True |
4801 | +envlist = py34, py35 |
4802 | +skip_missing_interpreters = True |
4803 | + |
4804 | +[testenv] |
4805 | +commands = py.test -v |
4806 | +deps = |
4807 | + -r{toxinidir}/requirements.txt |
4808 | + |
4809 | +[flake8] |
4810 | +exclude=docs |
4811 | |
4812 | === added directory 'wheelhouse' |
4813 | === added file 'wheelhouse/Jinja2-2.8.tar.gz' |
4814 | Binary files wheelhouse/Jinja2-2.8.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/Jinja2-2.8.tar.gz 2017-02-15 08:51:47 +0000 differ |
4815 | === added file 'wheelhouse/MarkupSafe-0.23.tar.gz' |
4816 | Binary files wheelhouse/MarkupSafe-0.23.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/MarkupSafe-0.23.tar.gz 2017-02-15 08:51:47 +0000 differ |
4817 | === added file 'wheelhouse/PyYAML-3.12.tar.gz' |
4818 | Binary files wheelhouse/PyYAML-3.12.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/PyYAML-3.12.tar.gz 2017-02-15 08:51:47 +0000 differ |
4819 | === added file 'wheelhouse/Tempita-0.5.2.tar.gz' |
4820 | Binary files wheelhouse/Tempita-0.5.2.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/Tempita-0.5.2.tar.gz 2017-02-15 08:51:47 +0000 differ |
4821 | === added file 'wheelhouse/charmhelpers-0.9.1.tar.gz' |
4822 | Binary files wheelhouse/charmhelpers-0.9.1.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/charmhelpers-0.9.1.tar.gz 2017-02-15 08:51:47 +0000 differ |
4823 | === added file 'wheelhouse/charms.reactive-0.4.5.tar.gz' |
4824 | Binary files wheelhouse/charms.reactive-0.4.5.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/charms.reactive-0.4.5.tar.gz 2017-02-15 08:51:47 +0000 differ |
4825 | === added file 'wheelhouse/netaddr-0.7.18.tar.gz' |
4826 | Binary files wheelhouse/netaddr-0.7.18.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/netaddr-0.7.18.tar.gz 2017-02-15 08:51:47 +0000 differ |
4827 | === added file 'wheelhouse/pip-8.1.2.tar.gz' |
4828 | Binary files wheelhouse/pip-8.1.2.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/pip-8.1.2.tar.gz 2017-02-15 08:51:47 +0000 differ |
4829 | === added file 'wheelhouse/pyaml-16.9.0.tar.gz' |
4830 | Binary files wheelhouse/pyaml-16.9.0.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/pyaml-16.9.0.tar.gz 2017-02-15 08:51:47 +0000 differ |
4831 | === added file 'wheelhouse/six-1.10.0.tar.gz' |
4832 | Binary files wheelhouse/six-1.10.0.tar.gz 1970-01-01 00:00:00 +0000 and wheelhouse/six-1.10.0.tar.gz 2017-02-15 08:51:47 +0000 differ |