Merge lp:~stub/charms/trusty/postgresql/built into lp:charms/trusty/postgresql

Proposed by Stuart Bishop
Status: Rejected
Rejected by: Stuart Bishop
Proposed branch: lp:~stub/charms/trusty/postgresql/built
Merge into: lp:charms/trusty/postgresql
Diff against target: 21013 lines (+9084/-10450)
120 files modified
.build.manifest (+642/-0)
.bzrignore (+3/-0)
.gitignore (+7/-0)
Makefile (+94/-43)
actions.yaml (+5/-5)
charm-helpers.yaml (+4/-2)
config.yaml (+501/-604)
hooks/bootstrap.py (+0/-57)
hooks/charmhelpers/__init__.py (+0/-38)
hooks/charmhelpers/context.py (+0/-206)
hooks/charmhelpers/contrib/__init__.py (+0/-15)
hooks/charmhelpers/contrib/charmsupport/__init__.py (+0/-15)
hooks/charmhelpers/contrib/charmsupport/nrpe.py (+0/-360)
hooks/charmhelpers/coordinator.py (+0/-607)
hooks/charmhelpers/core/__init__.py (+0/-15)
hooks/charmhelpers/core/decorators.py (+0/-57)
hooks/charmhelpers/core/files.py (+0/-45)
hooks/charmhelpers/core/fstab.py (+0/-134)
hooks/charmhelpers/core/hookenv.py (+0/-816)
hooks/charmhelpers/core/host.py (+0/-510)
hooks/charmhelpers/core/services/__init__.py (+0/-18)
hooks/charmhelpers/core/services/base.py (+0/-353)
hooks/charmhelpers/core/services/helpers.py (+0/-267)
hooks/charmhelpers/core/strutils.py (+0/-42)
hooks/charmhelpers/core/sysctl.py (+0/-56)
hooks/charmhelpers/core/templating.py (+0/-72)
hooks/charmhelpers/core/unitdata.py (+0/-477)
hooks/charmhelpers/fetch/__init__.py (+0/-448)
hooks/charmhelpers/fetch/archiveurl.py (+0/-167)
hooks/charmhelpers/fetch/bzrurl.py (+0/-78)
hooks/charmhelpers/fetch/giturl.py (+0/-73)
hooks/charmhelpers/payload/__init__.py (+0/-17)
hooks/charmhelpers/payload/archive.py (+0/-73)
hooks/charmhelpers/payload/execd.py (+0/-66)
hooks/client.py (+0/-182)
hooks/config-changed (+0/-23)
hooks/coordinator-relation-changed (+18/-0)
hooks/coordinator-relation-departed (+18/-0)
hooks/coordinator.py (+0/-19)
hooks/data-relation-changed (+0/-23)
hooks/data-relation-departed (+0/-23)
hooks/db-admin-relation-changed (+0/-23)
hooks/db-admin-relation-departed (+0/-23)
hooks/db-relation-changed (+0/-23)
hooks/db-relation-departed (+0/-23)
hooks/decorators.py (+0/-124)
hooks/defaulthook.py (+18/-0)
hooks/definitions.py (+0/-87)
hooks/helpers.py (+0/-151)
hooks/hook.template (+19/-0)
hooks/install (+0/-23)
hooks/leader-elected (+0/-23)
hooks/leader-settings-changed (+0/-23)
hooks/local-monitors-relation-changed (+0/-23)
hooks/local-monitors-relation-departed (+18/-0)
hooks/master-relation-changed (+0/-23)
hooks/master-relation-departed (+0/-23)
hooks/metrics.py (+0/-64)
hooks/nagios.py (+0/-84)
hooks/nrpe-external-master-relation-changed (+0/-23)
hooks/nrpe-external-master-relation-departed (+18/-0)
hooks/postgresql.py (+0/-693)
hooks/relations/block-storage/provides.py (+25/-0)
hooks/relations/syslog/provides.py (+73/-0)
hooks/replication-relation-changed (+0/-23)
hooks/replication-relation-departed (+0/-23)
hooks/replication.py (+0/-324)
hooks/service.py (+0/-948)
hooks/start (+0/-23)
hooks/stop (+0/-23)
hooks/storage.py (+0/-109)
hooks/syslog-relation-changed (+0/-23)
hooks/syslog-relation-departed (+0/-23)
hooks/syslogrel.py (+0/-72)
hooks/update-status (+19/-0)
hooks/upgrade-charm (+0/-24)
hooks/upgrade.py (+0/-121)
hooks/wal_e.py (+0/-129)
layer.yaml (+19/-0)
lib/charms/__init__.py (+2/-0)
lib/charms/apt.py (+180/-0)
lib/charms/coordinator.py (+135/-0)
lib/charms/layer/__init__.py (+21/-0)
lib/charms/layer/basic.py (+122/-0)
lib/charms/leadership.py (+58/-0)
lib/everyhook.py (+44/-0)
lib/pg_settings_9.5.json (+3145/-0)
lib/pgclient/hooks/hooks.py (+5/-6)
lib/pgdg.key (+39/-15)
lib/preflight.py (+39/-0)
lib/reactive/__init__.py (+30/-0)
lib/relations/__init__.py (+28/-0)
metadata.yaml (+35/-71)
reactive/__init__.py (+2/-0)
reactive/apt.py (+98/-0)
reactive/coordinator.py (+71/-0)
reactive/leadership.py (+68/-0)
reactive/postgresql/client.py (+235/-0)
reactive/postgresql/helpers.py (+120/-0)
reactive/postgresql/metrics.py (+74/-0)
reactive/postgresql/nagios.py (+103/-0)
reactive/postgresql/postgresql.py (+701/-0)
reactive/postgresql/preflight.py (+112/-0)
reactive/postgresql/replication.py (+485/-0)
reactive/postgresql/service.py (+900/-0)
reactive/postgresql/storage.py (+122/-0)
reactive/postgresql/syslog.py (+38/-0)
reactive/postgresql/upgrade.py (+118/-0)
reactive/postgresql/wal_e.py (+152/-0)
reactive/workloadstatus.py (+63/-0)
requirements.txt (+11/-0)
scripts/pgkillidle.py (+114/-0)
templates/postgres.cron.tmpl (+5/-0)
templates/rsyslog_forward.conf (+3/-3)
testing/amuletfixture.py (+1/-1)
tests/obsolete.py (+0/-963)
tests/test_integration.py (+40/-24)
tests/test_pg_hba_conf.py (+6/-3)
tests/test_postgresql.py (+45/-37)
tox.ini (+13/-0)
To merge this branch: bzr merge lp:~stub/charms/trusty/postgresql/built
Reviewer Review Type Date Requested Status
Review Queue (community) automated testing Needs Fixing
charmers Pending
Review via email: mp+282588@code.launchpad.net

Description of the change

This branch contains the charm generated from the reactive rewrite of the PostgreSQL charm with layers.

The source branch is at https://code.launchpad.net/~stub/charms/+source/postgresql/+git/postgresql/+ref/layered. This is probably what you want to look at.

The pure reactive rework (without layer support) is at https://code.launchpad.net/~stub/charms/+source/postgresql/+git/postgresql/+ref/reactive. It is probably uninteresting and harder to review due to embedded dependencies.

A mirror of the current PostgreSQL charm trunk is at https://code.launchpad.net/~stub/charms/+source/postgresql/+git/postgresql/+ref/master.

All three branches are in the same repository at lp:~stub/charms/+source/postgresql (or git+ssh://git.launchpad.net/~stub/charms/+source/postgresql if you don't have the lp: alias)

At least three layers will be broken out of this work (reactive/{apt,leadership,coordinator}.py) for use in other charms.

To post a comment you must log in.
Revision history for this message
Stuart Bishop (stub) wrote :

This work also added PostgreSQL 9.5 support.

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2189/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2170/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2201/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2182/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2205/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2185/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2209/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2245/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2224/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2246/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2225/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2248/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2227/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2260/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2239/

review: Needs Fixing (automated testing)
155. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2274/

review: Needs Fixing (automated testing)
156. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

157. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

158. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

159. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

160. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

161. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

162. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

163. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2253/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2302/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2336/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2281/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2348/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2349/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2350/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2362/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2315/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/2386/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2327/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2328/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/2329/

review: Needs Fixing (automated testing)
164. By Stuart Bishop

Merge trunk, resolve conflicts

165. By Stuart Bishop

delint, per trunk

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-azure/2423/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/3021/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-joyent/2605/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3091/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-power8/61/

review: Needs Fixing (automated testing)
166. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:master

Revision history for this message
Stuart Bishop (stub) wrote :

I've synced this with v3.1.2 of upstream. In particular, it includes the updated package signing key required to install non-default versions (9.1, 9.2, 9.4, 9.5)

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-power8/62/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

The results (PASS) are in and available here: http://juju-ci.vapour.ws:8080/job/charm-bundle-test-joyent/2677/

review: Approve (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3210/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-azure/2512/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/3247/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3301/

review: Needs Fixing (automated testing)
167. By Stuart Bishop

Ignore .tox directory when tests repackage the charm

Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-joyent/2782/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-lxc/3299/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-azure/2557/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3363/

review: Needs Fixing (automated testing)
Revision history for this message
Review Queue (review-queue) wrote :

This item has failed automated testing! Results available here http://juju-ci.vapour.ws:8080/job/charm-bundle-test-aws/3656/

review: Needs Fixing (automated testing)

Unmerged revisions

167. By Stuart Bishop

Ignore .tox directory when tests repackage the charm

166. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:master

165. By Stuart Bishop

delint, per trunk

164. By Stuart Bishop

Merge trunk, resolve conflicts

163. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

162. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

161. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

160. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

159. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

158. By Stuart Bishop

charm build of git+ssh://<email address hidden>/~stub/charms/+source/postgresql:layered

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.build.manifest'
2--- .build.manifest 1970-01-01 00:00:00 +0000
3+++ .build.manifest 2016-03-25 03:37:00 +0000
4@@ -0,0 +1,642 @@
5+{
6+ "layers": [
7+ "layer:basic",
8+ "layer:apt",
9+ "layer:leadership",
10+ "layer:coordinator",
11+ "postgresql",
12+ "build"
13+ ],
14+ "signatures": {
15+ "reactive/postgresql/syslog.py": [
16+ "postgresql",
17+ "static",
18+ "89d75ed073122877ab54939be957235688976c0073b5fbab0cd79fd9951d0628"
19+ ],
20+ "lib/reactive/__init__.py": [
21+ "postgresql",
22+ "static",
23+ "dc1e89fc8c1a137a08dda8091eb115c120888e7044220f58946b6aa0d7a6360a"
24+ ],
25+ "hooks/leader-settings-changed": [
26+ "layer:coordinator",
27+ "static",
28+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
29+ ],
30+ "reactive/postgresql/upgrade.py": [
31+ "postgresql",
32+ "static",
33+ "b17288705c3075e18340822a64ccd5aea50ca2e108f10ecaf5b1f9aa20be2b0f"
34+ ],
35+ "hooks/relations/syslog/__init__.py": [
36+ "postgresql",
37+ "static",
38+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
39+ ],
40+ "hooks/leader-elected": [
41+ "layer:coordinator",
42+ "static",
43+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
44+ ],
45+ "wheelhouse/Jinja2-2.8.tar.gz": [
46+ "layer:basic",
47+ "dynamic",
48+ "bc1ff2ff88dbfacefde4ddde471d1417d3b304e8df103a7a9437d47269201bf4"
49+ ],
50+ "lib/pgclient/hooks/db-admin-relation-broken": [
51+ "postgresql",
52+ "static",
53+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
54+ ],
55+ "lib/pgdg.key": [
56+ "postgresql",
57+ "static",
58+ "628a1e23fe2be9162b36326191b9cb618d6fc4949c333a97b906985e7249030d"
59+ ],
60+ "hooks/db-admin-relation-changed": [
61+ "postgresql",
62+ "static",
63+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
64+ ],
65+ "reactive/postgresql/client.py": [
66+ "postgresql",
67+ "static",
68+ "02adee770a91d2a043be649fe6c1eed82517672ddff7f58b744f3f63ec32703f"
69+ ],
70+ "lib/preflight.py": [
71+ "postgresql",
72+ "static",
73+ "44c7c8e9f5bcca5a7004578d171ee16b42898d9f8d9d6bf4126c6e2c21cdaa56"
74+ ],
75+ "hooks/data-relation-departed": [
76+ "postgresql",
77+ "static",
78+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
79+ ],
80+ "lib/pgclient/hooks/hooks.py": [
81+ "postgresql",
82+ "static",
83+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
84+ ],
85+ "actions.yaml": [
86+ "postgresql",
87+ "dynamic",
88+ "346e163cf643fa15e7f8d17dfcbf0a555fefc0ad82af6aec8575eeced58b356a"
89+ ],
90+ "lib/charms/__init__.py": [
91+ "layer:coordinator",
92+ "static",
93+ "4032724b98ed267185b12b8ec78dd9bcb0423ab1c89a6f0dad99397fd51159d9"
94+ ],
95+ "templates/recovery.conf.tmpl": [
96+ "postgresql",
97+ "static",
98+ "a441d24c1baf969e2db38b94aa7525c5934f8bf3f9687202070d7c41b2df85b2"
99+ ],
100+ "testing/__init__.py": [
101+ "postgresql",
102+ "static",
103+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
104+ ],
105+ "hooks/defaulthook.py": [
106+ "postgresql",
107+ "static",
108+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
109+ ],
110+ "copyright": [
111+ "postgresql",
112+ "static",
113+ "5d53c0caa528000229bd4db076ee0516ecd4a88146196edb31740365bb2899ec"
114+ ],
115+ "hooks/replication-relation-departed": [
116+ "postgresql",
117+ "static",
118+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
119+ ],
120+ "requirements.txt": [
121+ "postgresql",
122+ "static",
123+ "d690f4480ec2ab809634d59b7bb0f252bc3dbf96f28442087c8c2363712d5458"
124+ ],
125+ ".gitignore": [
126+ "postgresql",
127+ "static",
128+ "4456de55b108d6e2e1d507553414c40eb02817632d6374fba83ef46ce68944de"
129+ ],
130+ "hooks/install": [
131+ "layer:basic",
132+ "static",
133+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
134+ ],
135+ "wheelhouse/MarkupSafe-0.23.tar.gz": [
136+ "layer:basic",
137+ "dynamic",
138+ "a4ec1aff59b95a14b45eb2e23761a0179e98319da5a7eb76b56ea8cdc7b871c3"
139+ ],
140+ "hooks/local-monitors-relation-changed": [
141+ "postgresql",
142+ "static",
143+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
144+ ],
145+ "templates/postgres.cron.tmpl": [
146+ "postgresql",
147+ "static",
148+ "607f45a45d6b725801d087d7b3aea24ab6cfd83e13419aef5aed8ceec0aa4755"
149+ ],
150+ "charm-helpers.yaml": [
151+ "postgresql",
152+ "static",
153+ "623de7525d60892fe653fac1b6fcd82df1044138c47bdab7dec12545ec9a53d6"
154+ ],
155+ "bzrsync2.sh": [
156+ "postgresql",
157+ "static",
158+ "f4998a90b51ceb6b3c1d9d0fd9e1427ed9675b81012166f3327a1c340b8bbb47"
159+ ],
160+ "hooks/db-relation-departed": [
161+ "postgresql",
162+ "static",
163+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
164+ ],
165+ "lib/pgclient/config.yaml": [
166+ "postgresql",
167+ "static",
168+ "aeae87eca107835959d830dd1afd1a03ecbaf03fb358a944fe6788dbf9c4f229"
169+ ],
170+ "icon.svg": [
171+ "postgresql",
172+ "static",
173+ "00ffc4f772f0e5d37c6c8a2a472488c9c9e396af38ddaa251f92b9e9dbda0358"
174+ ],
175+ "lib/pgclient/hooks/start": [
176+ "postgresql",
177+ "static",
178+ "88b982246c3bc30f59c18c20e9f95c257c5c8d6114dcc300a5652931d10d7052"
179+ ],
180+ "config.yaml": [
181+ "postgresql",
182+ "dynamic",
183+ "cff7292ea8bfa58b9445cd04eb45cf668cd62906cd7c345fafd0410c6a54453c"
184+ ],
185+ "hooks/relations/block-storage/__init__.py": [
186+ "postgresql",
187+ "static",
188+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
189+ ],
190+ "reactive/coordinator.py": [
191+ "layer:coordinator",
192+ "static",
193+ "18cda7ddf00ae0e47578d489fc3ebb376b4428cd0559797a87ddbead54360d02"
194+ ],
195+ "reactive/workloadstatus.py": [
196+ "postgresql",
197+ "static",
198+ "ec87dafd476b3ab14e89a0c776c38f3e77cbec3adfda3a43cec8db11ca19fe9b"
199+ ],
200+ "lib/pgclient/hooks/db-admin-relation-joined": [
201+ "postgresql",
202+ "static",
203+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
204+ ],
205+ "hooks/hook.template": [
206+ "layer:basic",
207+ "static",
208+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
209+ ],
210+ "hooks/data-relation-changed": [
211+ "postgresql",
212+ "static",
213+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
214+ ],
215+ "hooks/relations/block-storage/provides.py": [
216+ "postgresql",
217+ "static",
218+ "21bb1924a348ffd7900b19d625ab096c91faf84426d2f9ce69a34043963de862"
219+ ],
220+ "reactive/__init__.py": [
221+ "postgresql",
222+ "static",
223+ "4032724b98ed267185b12b8ec78dd9bcb0423ab1c89a6f0dad99397fd51159d9"
224+ ],
225+ "hooks/relations/syslog/provides.py": [
226+ "postgresql",
227+ "static",
228+ "cfd5c0cbf82fe1715a294eb264a85b89717de596263e7a9f5bec576a240b74f5"
229+ ],
230+ "hooks/coordinator-relation-changed": [
231+ "layer:coordinator",
232+ "static",
233+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
234+ ],
235+ "lib/juju-deployer-wrapper.py": [
236+ "postgresql",
237+ "static",
238+ "b33d9d4c0ae65b8b0084854fe1a22c468646699179fc7b25710b2b637dc7b1ba"
239+ ],
240+ "hooks/syslog-relation-changed": [
241+ "postgresql",
242+ "static",
243+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
244+ ],
245+ "reactive/postgresql/helpers.py": [
246+ "postgresql",
247+ "static",
248+ "f9698479eddd59ce1f1033a6bcba35126cc0fb84312401c2fcefef7226bd01ee"
249+ ],
250+ "reactive/postgresql/nagios.py": [
251+ "postgresql",
252+ "static",
253+ "dcf2117295d6b78f335911a10f476e75decc4197f4c02c2a60036ba4edf145f4"
254+ ],
255+ "hooks/update-status": [
256+ "layer:basic",
257+ "static",
258+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
259+ ],
260+ "templates/metrics_cronjob.template": [
261+ "postgresql",
262+ "static",
263+ "c607c576dea12923a701855f97ea748e1340e3c14309dd2cd6dd912e0d522e59"
264+ ],
265+ "reactive/postgresql/metrics.py": [
266+ "postgresql",
267+ "static",
268+ "c6b957110b97cf6dbf8e10e72a552b334bae12239d45f290170dfbef2537f86a"
269+ ],
270+ "README.md": [
271+ "postgresql",
272+ "static",
273+ "fd4e5ec48f069bb42dd0973a4c373be4232cd5bfec4270c5035a3f320f9e693c"
274+ ],
275+ "lib/pgclient/hooks/install": [
276+ "postgresql",
277+ "static",
278+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
279+ ],
280+ ".bzrignore": [
281+ "postgresql",
282+ "static",
283+ "0add588743632fea5f0753d45d9b2f1d78c47689cf48d67744a3c3af55548404"
284+ ],
285+ "hooks/replication-relation-changed": [
286+ "postgresql",
287+ "static",
288+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
289+ ],
290+ "hooks/nrpe-external-master-relation-departed": [
291+ "postgresql",
292+ "static",
293+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
294+ ],
295+ "reactive/postgresql/replication.py": [
296+ "postgresql",
297+ "static",
298+ "0ad94373d23a030c40d083df70892a30879faa3ebc7091d4471b8b931a6298ac"
299+ ],
300+ "wheelhouse/charms.reactive-0.4.0.tar.gz": [
301+ "layer:basic",
302+ "dynamic",
303+ "07624f413b0397e5ac8e2baec8a5d2727949733516fc6786ffe5b72cc556dfa2"
304+ ],
305+ "hooks/db-relation-changed": [
306+ "postgresql",
307+ "static",
308+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
309+ ],
310+ "lib/pg_settings_9.1.json": [
311+ "postgresql",
312+ "static",
313+ "cf8d3deb211c752dd747f19305399a0a27863a013a78dbd417c51dec818fd6d4"
314+ ],
315+ "lib/pgclient/hooks/upgrade-charm": [
316+ "postgresql",
317+ "static",
318+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
319+ ],
320+ "scripts/pgbackup.py": [
321+ "postgresql",
322+ "static",
323+ "75d841e055226929d365f6d16b745f814a33dd3f0132e94db3b6d63c8bdbf207"
324+ ],
325+ "lib/everyhook.py": [
326+ "postgresql",
327+ "static",
328+ "eea4308c7609c0a6989c829e6c64eac5686fad56106935b65ea6b191a50f8f34"
329+ ],
330+ "lib/pg_settings_9.5.json": [
331+ "postgresql",
332+ "static",
333+ "e07ae636acf64402fc837b3af4d1e1312755dfca73489739162a4c7519029d9c"
334+ ],
335+ "reactive/postgresql/service.py": [
336+ "postgresql",
337+ "static",
338+ "4d466e671eaa46d66d7c09362114e95e787b1958e79a8036fdb8054526461d3f"
339+ ],
340+ "scripts/pgkillidle.py": [
341+ "postgresql",
342+ "static",
343+ "eab206ea6846a5f59bb079a336ff2f4ccf3b9467d99626798df6062498d74d65"
344+ ],
345+ "wheelhouse/charmhelpers-0.7.0.tar.gz": [
346+ "layer:basic",
347+ "dynamic",
348+ "3327d1e3e4bcc5d22aa8cf6748cc1d7e400f8e3781755ac138229dad1390d834"
349+ ],
350+ "actions/replication-resume": [
351+ "postgresql",
352+ "static",
353+ "d5cc0041d00122974ceaab3e15d644f34cb5cd5a92d24897a167c952b93595a2"
354+ ],
355+ "wheelhouse/pyaml-15.8.2.tar.gz": [
356+ "layer:basic",
357+ "dynamic",
358+ "9c54fb5f17b58572c4cef50affea60bb73f445ab153580dac07a12383712b5b8"
359+ ],
360+ "reactive/postgresql/__init__.py": [
361+ "postgresql",
362+ "static",
363+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
364+ ],
365+ "wheelhouse/pip-7.1.2.tar.gz": [
366+ "layer:basic",
367+ "dynamic",
368+ "ca047986f0528cfa975a14fb9f7f106271d4e0c3fe1ddced6c1db2e7ae57a477"
369+ ],
370+ "tests/test_postgresql.py": [
371+ "postgresql",
372+ "static",
373+ "dd537d9537fc788238adc0130c4f0e38971ced8c77d170c68e8e9d49e08342c8"
374+ ],
375+ "Makefile": [
376+ "postgresql",
377+ "static",
378+ "d592b55f279fe62868eee02bf3bb92910154ad4f6429ae5e620a1290cbe9ba99"
379+ ],
380+ "reactive/postgresql/wal_e.py": [
381+ "postgresql",
382+ "static",
383+ "731e3428a672e377f651bf0892c96fa1127e4473baee5d8638eb78e463db5b97"
384+ ],
385+ "lib/pg_settings_9.3.json": [
386+ "postgresql",
387+ "static",
388+ "e3acd92c66413d9876cec0517a65219a8437756138da1bd06c52041275c477fc"
389+ ],
390+ "reactive/postgresql/preflight.py": [
391+ "postgresql",
392+ "static",
393+ "08ad2d066de3caa0d2c7750cae99b347041186a417d679bb32072416f06ed7a0"
394+ ],
395+ "hooks/coordinator-relation-departed": [
396+ "layer:coordinator",
397+ "static",
398+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
399+ ],
400+ "hooks/upgrade-charm": [
401+ "layer:basic",
402+ "static",
403+ "8b9c153a61cc78067af8d016a8fa1c764caad3a40c82bb0b5121fc4c4c6a04f1"
404+ ],
405+ "wheelhouse/PyYAML-3.11.tar.gz": [
406+ "layer:basic",
407+ "dynamic",
408+ "c36c938a872e5ff494938b33b14aaa156cb439ec67548fcab3535bb78b0846e8"
409+ ],
410+ "hooks/db-admin-relation-departed": [
411+ "postgresql",
412+ "static",
413+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
414+ ],
415+ "tests/test_integration.py": [
416+ "postgresql",
417+ "static",
418+ "2b236c8720ce974c62febd7ed2abcde90e25aa7c69152b749555940c2b536b7d"
419+ ],
420+ "reactive/leadership.py": [
421+ "layer:leadership",
422+ "static",
423+ "e2b233cf861adc3b2d9e9c062134ce2f104953f03283cdddd88f49efee652e8f"
424+ ],
425+ "lib/pgclient/metadata.yaml": [
426+ "postgresql",
427+ "static",
428+ "526b5c1525b53f9f10b142933a3eed00123a1e6a6b0e700f101bdf7014d5881e"
429+ ],
430+ "lib/pgclient/hooks/db-relation-joined": [
431+ "postgresql",
432+ "static",
433+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
434+ ],
435+ "files/metrics/postgres_to_statsd.py": [
436+ "postgresql",
437+ "static",
438+ "974ef68ae533abba2d345ba475bba3ad88b629ed13ade1f92fe928f5df814eda"
439+ ],
440+ "tox.ini": [
441+ "postgresql",
442+ "static",
443+ "518188cb48bd2bb184ddd981360768449cff2b517f2bebb1b8166ff3e5774261"
444+ ],
445+ "wheelhouse/Tempita-0.5.2.tar.gz": [
446+ "layer:basic",
447+ "dynamic",
448+ "cacecf0baa674d356641f1d406b8bff1d756d739c46b869a54de515d08e6fc9c"
449+ ],
450+ "lib/pgclient/hooks/db-admin-relation-changed": [
451+ "postgresql",
452+ "static",
453+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
454+ ],
455+ "hooks/nrpe-external-master-relation-changed": [
456+ "postgresql",
457+ "static",
458+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
459+ ],
460+ "lib/charms/leadership.py": [
461+ "layer:leadership",
462+ "static",
463+ "12b52fa768c3e9072556ac6f46f68129dc762f89eb806d8dee921c86497f6707"
464+ ],
465+ "wheelhouse/netaddr-0.7.18.tar.gz": [
466+ "layer:basic",
467+ "dynamic",
468+ "a1f5c9fcf75ac2579b9995c843dade33009543c04f218ff7c007b3c81695bd19"
469+ ],
470+ "hooks/local-monitors-relation-departed": [
471+ "postgresql",
472+ "static",
473+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
474+ ],
475+ "lib/pgclient/hooks/stop": [
476+ "postgresql",
477+ "static",
478+ "88b982246c3bc30f59c18c20e9f95c257c5c8d6114dcc300a5652931d10d7052"
479+ ],
480+ "lib/pgclient/hooks/db-relation-broken": [
481+ "postgresql",
482+ "static",
483+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
484+ ],
485+ "templates/pg_backup_job.tmpl": [
486+ "postgresql",
487+ "static",
488+ "4c136bed07da283c5703c68fbd9afdb688c819d8bb0f1af44d483bd284bacfd3"
489+ ],
490+ ".build.manifest": [
491+ "build",
492+ "dynamic",
493+ "unchecked"
494+ ],
495+ "hooks/config-changed": [
496+ "layer:basic",
497+ "static",
498+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
499+ ],
500+ "lib/charms/layer/__init__.py": [
501+ "layer:basic",
502+ "static",
503+ "4ffd74384bd81d737572acf2d14fe55431eab8f49d6212d9aabedf24e1d992b2"
504+ ],
505+ "lib/cache_settings.py": [
506+ "postgresql",
507+ "static",
508+ "2e4d7ffa99cb4da680cf009da6f4fd3c6ee6eff46d7b698f0ef5c4bf978b407a"
509+ ],
510+ "hooks/stop": [
511+ "layer:basic",
512+ "static",
513+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
514+ ],
515+ "reactive/apt.py": [
516+ "layer:apt",
517+ "static",
518+ "d47770a6a44c723082a6a7a658679e15b73f0f2280d4662bc80c2935823dc20a"
519+ ],
520+ "lib/charms/layer/basic.py": [
521+ "layer:basic",
522+ "static",
523+ "606cc4841d1d31036cf1e771fa0ef9f67b6a3ee7d0fbd4fa07f2aa2739514842"
524+ ],
525+ "tests/tests.yaml": [
526+ "postgresql",
527+ "static",
528+ "b0a7b70178b3a80da4f0625663448fe5973ba093c838d6ba5d115c546d8a29f0"
529+ ],
530+ "lib/pg_settings_9.2.json": [
531+ "postgresql",
532+ "static",
533+ "aa4b49cb48f3ae388bc685c31ddb7f6befc55e74baa9317f80eb117cec7d03a2"
534+ ],
535+ "hooks/syslog-relation-departed": [
536+ "postgresql",
537+ "static",
538+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
539+ ],
540+ "lib/pg_settings_9.4.json": [
541+ "postgresql",
542+ "static",
543+ "eef6c9a38014506c09ec56972475fd41df129a1f616e807a18d626687bed4eae"
544+ ],
545+ "metadata.yaml": [
546+ "postgresql",
547+ "dynamic",
548+ "aa031717098f9a0ec7f88ba8fc49d9684660cc965b21738db8610e606da741db"
549+ ],
550+ "lib/pgclient/hooks/db-relation-changed": [
551+ "postgresql",
552+ "static",
553+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
554+ ],
555+ "hooks/relations/__init__.py": [
556+ "postgresql",
557+ "static",
558+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
559+ ],
560+ "testing/amuletfixture.py": [
561+ "postgresql",
562+ "static",
563+ "1d9f021442fcdde9c12d3aa72f1939003b689ea3e204617fed9194bdf18f69f3"
564+ ],
565+ "hooks/master-relation-changed": [
566+ "postgresql",
567+ "static",
568+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
569+ ],
570+ "actions/actions.py": [
571+ "postgresql",
572+ "static",
573+ "d5cc0041d00122974ceaab3e15d644f34cb5cd5a92d24897a167c952b93595a2"
574+ ],
575+ "lib/charms/coordinator.py": [
576+ "layer:coordinator",
577+ "static",
578+ "0bbd2f3664f7f067bb037cda88fc67d18c57f37f7b246aa053578dfd8c3e2966"
579+ ],
580+ "templates/rsyslog_forward.conf": [
581+ "postgresql",
582+ "static",
583+ "b5c3685640c128e4fd956bab36c9ef8cc60e229acb524d0d6eccf27c67307709"
584+ ],
585+ "actions/replication-pause": [
586+ "postgresql",
587+ "static",
588+ "d5cc0041d00122974ceaab3e15d644f34cb5cd5a92d24897a167c952b93595a2"
589+ ],
590+ "lib/pgclient/hooks/config-changed": [
591+ "postgresql",
592+ "static",
593+ "10bb57be61d28fd9b4c8d5207596c24ea30ee1b139a40aeb06055d7efe9b6fe3"
594+ ],
595+ "templates/nrpe_service.tmpl": [
596+ "postgresql",
597+ "static",
598+ "6e33f1818251131a644527c87ea47bd1fc690cefd9fe0c06b120ea9f25d78f92"
599+ ],
600+ "reactive/postgresql/storage.py": [
601+ "postgresql",
602+ "static",
603+ "ab6e9f2bb05a56a6d190cedf95c7c696ef2a4b53732118718075b76e8b845539"
604+ ],
605+ "hooks/start": [
606+ "layer:basic",
607+ "static",
608+ "3a854a5fd180c5bacbb80b501d968a8d8bdc1468361e7123ea4022a1e0363c9a"
609+ ],
610+ "reactive/postgresql/postgresql.py": [
611+ "postgresql",
612+ "static",
613+ "ee83764d5615b5a8563a391187681504ff0482e8a970eaa2ab76685a4ed4ddba"
614+ ],
615+ "hooks/master-relation-departed": [
616+ "postgresql",
617+ "static",
618+ "e5138d13492aa9a90379e8fce4a85c612481e7bc27a49958edbbfcaaf06f03a6"
619+ ],
620+ "wheelhouse/six-1.10.0.tar.gz": [
621+ "layer:basic",
622+ "dynamic",
623+ "105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a"
624+ ],
625+ "lib/relations/__init__.py": [
626+ "postgresql",
627+ "static",
628+ "9cd78d4bfba601cad3c1f5b2f7a1efb64b01dbad9f07cfee922d819c673dd2f5"
629+ ],
630+ "layer.yaml": [
631+ "postgresql",
632+ "dynamic",
633+ "b2c05147d5e64c9731d85d5905bc3185e5fe79bf1de4296ad3eb7d7edd137274"
634+ ],
635+ "tests/test_pg_hba_conf.py": [
636+ "postgresql",
637+ "static",
638+ "c1636786ad8d3a8a721faa5633afebb105abe58f9bbfd83e20632abac6bdfa4f"
639+ ],
640+ "lib/charms/apt.py": [
641+ "layer:apt",
642+ "static",
643+ "9ed953bd9411b982561646607212eaa37494f7a8d1696e15832d8ea0ec23c762"
644+ ]
645+ }
646+}
647\ No newline at end of file
648
649=== modified file '.bzrignore'
650--- .bzrignore 2015-08-20 09:57:11 +0000
651+++ .bzrignore 2016-03-25 03:37:00 +0000
652@@ -2,5 +2,8 @@
653 hooks/_trial_temp
654 hooks/local_state.pickle
655 lib/pgclient/hooks/charmhelpers/
656+lib/testdeps
657 coverage
658 .coverage
659+.unit-state.db
660+.tox
661
662=== added file '.gitignore'
663--- .gitignore 1970-01-01 00:00:00 +0000
664+++ .gitignore 2016-03-25 03:37:00 +0000
665@@ -0,0 +1,7 @@
666+*~
667+*.pyc
668+.coverage
669+.unit-state.db
670+bzrsync2.sh
671+lib/testdeps
672+lib/pgclient/hooks/charmhelpers
673
674=== modified file 'Makefile'
675--- Makefile 2015-11-12 05:16:44 +0000
676+++ Makefile 2016-03-25 03:37:00 +0000
677@@ -1,8 +1,13 @@
678 CHARM_DIR := $(shell pwd)
679 TEST_TIMEOUT := 900
680-SERIES := $(shell juju get-environment default-series)
681+#SERIES := $(shell juju get-environment default-series)
682+SERIES := trusty
683 HOST_SERIES := $(shell lsb_release -sc)
684
685+BUILD_ROOT=/home/stub/charms/built
686+BUILD_DIR=${BUILD_ROOT}/${SERIES}/postgresql
687+
688+
689 # /!\ Ensure that errors early in pipes cause failures, rather than
690 # overridden by the last stage of the pipe. cf. 'test.py | ts'
691 SHELL := /bin/bash
692@@ -29,20 +34,15 @@
693 test: testdeps lint unittest integration
694
695 testdeps:
696+ifeq ($(HOST_SERIES),trusty)
697+ sudo apt-get install -y python-tox python3-psycopg2 bzr moreutils \
698+ software-properties-common python3-flake8
699+else
700+ sudo apt-get install -y tox python3-psycopg2 bzr moreutils \
701+ software-properties-common python3-flake8
702+endif
703 sudo add-apt-repository -y ppa:juju/stable
704- sudo add-apt-repository -y ppa:stub/juju
705- sudo apt-get update
706-ifeq ($(HOST_SERIES),trusty)
707- sudo apt-get install -y \
708- python3-psycopg2 python3-nose python3-flake8 amulet \
709- python3-jinja2 python3-yaml juju-wait bzr python3-amulet \
710- python-swiftclient moreutils
711-else
712- sudo apt-get install -y \
713- python3-psycopg2 python3-nose python3-flake8 amulet \
714- python3-jinja2 python3-yaml juju-wait bzr python3-amulet \
715- python3-nose-cov python3-nose-timer python-swiftclient moreutils
716-endif
717+ sudo apt-get install charm-tools
718
719 lint:
720 @echo "Charm Proof"
721@@ -50,8 +50,32 @@
722 @echo "Lint check (flake8)"
723 @flake8 -v \
724 --ignore=E402 \
725- --exclude=hooks/charmhelpers,__pycache__ \
726- hooks actions testing tests
727+ --exclude=lib/testdeps,lib/pgclient/hooks/charmhelpers,lib/charms,__pycache__,.tox \
728+ hooks actions testing tests reactive lib
729+
730+# Clean crud from running tests etc.
731+buildclean:
732+ rm -rf ${BUILD_DIR}/lib/pgclient/hooks/charmhelpers
733+ rm -rf ${BUILD_DIR}/.tox
734+ rm -rf ${BUILD_DIR}/.cache
735+ rm -rf ${BUILD_DIR}/.unit-state.db
736+ rm -rf ${BUILD_DIR}/.coverage
737+
738+build: buildclean
739+ @echo "Building charm"
740+ charm build -o ${BUILD_ROOT} -s ${SERIES}
741+
742+fbuild: buildclean
743+ @echo "Forcefully building charm"
744+ charm build -o ${BUILD_ROOT} -s ${SERIES} --force
745+
746+# Build with a custom charms.reactive
747+custombuild: fbuild
748+ rm -f ${BUILD_DIR}/wheelhouse/charms.reactive*
749+ rsync -rav --delete \
750+ --exclude='*.pyc' --exclude='__pycache__' --exclude='*~' \
751+ ${HOME}/charms/charms.reactive/charms/reactive/ \
752+ ${BUILD_DIR}/lib/charms/reactive/
753
754 _co=,
755 _empty=
756@@ -60,38 +84,65 @@
757 TESTFILES=$(filter-out %/test_integration.py,$(wildcard tests/test_*.py))
758 PACKAGES=$(subst $(_sp),$(_co),$(notdir $(basename $(wildcard hooks/*.py))))
759
760-NOSE := nosetests3 -sv
761-ifeq ($(HOST_SERIES),trusty)
762-TIMING_NOSE := nosetests3 -sv
763-else
764-TIMING_NOSE := nosetests3 -sv --with-timer
765-endif
766-
767-unittest:
768- ${NOSE} ${TESTFILES} --cover-package=${PACKAGES} \
769- --with-coverage --cover-branches
770+tox: .tox/testenv/bin/python3
771+
772+.tox/testenv/bin/python3: requirements.txt
773+ tox --notest -r
774+
775+# Put the testenv on the PATH so the juju-wait plugin is found.
776+export PATH := .tox/testenv/bin:$(PATH)
777+
778+NOSE := .tox/testenv/bin/nosetests -sv
779+TIMING_NOSE := ${NOSE} --with-timer
780+
781+unittest: tox
782+ ${NOSE} ${TESTFILES}
783 @echo OK: Unit tests pass `date`
784
785-coverage:
786- ${NOSE} ${TESTFILES} --cover-package=${PACKAGES} \
787- --with-coverage --cover-branches \
788- --cover-erase --cover-html --cover-html-dir=coverage \
789- --cover-min-percentage=100 || \
790- (gnome-open coverage/index.html; false)
791-
792-integration:
793+# Coverage broken?
794+#
795+# unittest: tox
796+# ${NOSE} ${TESTFILES} --cover-package=${PACKAGES} \
797+# --with-coverage --cover-branches
798+# @echo OK: Unit tests pass `date`
799+#
800+# coverage: tox
801+# ${NOSE} ${TESTFILES} --cover-package=${PACKAGES} \
802+# --with-coverage --cover-branches \
803+# --cover-erase --cover-html --cover-html-dir=coverage \
804+# --cover-min-percentage=100 || \
805+# (gnome-open coverage/index.html; false)
806+
807+
808+# We need to unpack charmhelpers so the old non-reactive test client
809+# charm works (rather than embed another copy).
810+client-charmhelpers:
811+ tar -xz --strip-components 1 \
812+ -f wheelhouse/charmhelpers-*.tar.gz \
813+ -C lib/pgclient/hooks --wildcards '*/charmhelpers'
814+
815+integration-deps: tox client-charmhelpers
816+
817+integration: integration-deps
818 ${TIMING_NOSE} tests/test_integration.py 2>&1 | ts
819+
820+# More overheads, but better progress reporting
821+integration_breakup: integration-deps
822+ ${NOSE} tests/test_integration.py:PG93Tests 2>&1 | ts
823+ ${NOSE} tests/test_integration.py:PG93MultiTests 2>&1 | ts
824+ ${NOSE} tests/test_integration.py:UpgradedCharmTests 2>&1 | ts
825+ ${NOSE} tests/test_integration.py:PG91Tests 2>&1 | ts
826+ ${NOSE} tests/test_integration.py:PG91MultiTests 2>&1 | ts
827+ ${NOSE} tests/test_integration.py:PG95Tests 2>&1 | ts
828+ ${NOSE} tests/test_integration.py:PG95MultiTests 2>&1 | ts
829+ ${NOSE} tests/test_integration.py:PG94Tests 2>&1 | ts
830+ ${NOSE} tests/test_integration.py:PG94MultiTests 2>&1 | ts
831+ ${NOSE} tests/test_integration.py:PG92Tests 2>&1 | ts
832+ ${NOSE} tests/test_integration.py:PG92MultiTests 2>&1 | ts
833 @echo OK: Integration tests pass `date`
834
835-sync:
836- @bzr cat \
837- lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py \
838- > .charm_helpers_sync.py
839- @python .charm_helpers_sync.py -c charm-helpers.yaml
840- @rm .charm_helpers_sync.py
841-
842-
843 # These targets are to separate the test output in the Charm CI system
844-test_integration.py%:
845+# eg. 'make test_integration.py:PG93Tests'
846+test_integration.py%: integration-deps
847 ${TIMING_NOSE} tests/$@ 2>&1 | ts
848 @echo OK: $@ tests pass `date`
849
850=== modified file 'actions.yaml'
851--- actions.yaml 2015-08-11 10:36:28 +0000
852+++ actions.yaml 2016-03-25 03:37:00 +0000
853@@ -1,8 +1,8 @@
854-replication-pause:
855- description: Pause replication replay on a hot standby unit.
856-replication-resume:
857- description: Resume replication replay on a hot standby unit.
858-
859+"replication-pause":
860+ "description": "Pause replication replay on a hot standby unit."
861+"replication-resume":
862+ "description": "Resume replication replay on a hot standby unit."
863+
864 # Revisit this when actions are more mature. Per Bug #1483525, it seems
865 # impossible to return filenames in our results.
866 # backup:
867
868=== modified file 'charm-helpers.yaml'
869--- charm-helpers.yaml 2015-08-06 12:08:02 +0000
870+++ charm-helpers.yaml 2016-03-25 03:37:00 +0000
871@@ -1,6 +1,8 @@
872-destination: hooks/charmhelpers
873-branch: lp:~stub/charm-helpers/integration
874+destination: lib/charmhelpers
875+branch: lp:charm-helpers
876+# branch: lp:~stub/charm-helpers/integration
877 include:
878+ - cli
879 - context
880 - coordinator
881 - core
882
883=== modified file 'config.yaml'
884--- config.yaml 2016-02-01 07:02:57 +0000
885+++ config.yaml 2016-03-25 03:37:00 +0000
886@@ -1,173 +1,164 @@
887-options:
888- admin_addresses:
889- default: ""
890- type: string
891- description: >
892- A comma-separated list of IP Addresses (or single IP) admin tools
893- like pgAdmin3 will connect from. The IP addresses added here will
894- be included in the pg_hba.conf file allowing ip connections to all
895- databases on the server from the given IP addresses using md5
896- password encryption. IP address ranges are also supported, using
897- the standard format described in the PostgreSQL reference guide.
898- locale:
899- default: "C"
900- type: string
901- description: >
902- Locale of service, defining language, default collation order,
903- and default formatting of numbers, currency, dates & times. Can only be
904- set when deploying the first unit of a service.
905- encoding:
906- default: "UTF-8"
907- type: string
908- description: >
909- Default encoding used to store text in this service. Can only be
910- set when deploying the first unit of a service.
911- relation_database_privileges:
912- default: "ALL"
913- type: string
914- description: >
915- A comma-separated list of database privileges to grant to relation
916- users on their databases. The defaults allow to connect to the
917- database (CONNECT), create objects such as tables (CREATE), and
918- create temporary tables (TEMPORARY). Client charms that create
919- objects in the database are responsible to granting suitable
920- access on those objects to other roles and users (or PUBLIC) using
921- standard GRANT statements.
922- extra_packages:
923- default: ""
924- type: string
925- description: >
926- Space separated list of extra packages to install.
927- dumpfile_location:
928- default: "None"
929- type: string
930- description: >
931- Path to a dumpfile to load into DB when service is initiated.
932- version:
933- default: ""
934- type: string
935- description: >
936- Version of PostgreSQL that we want to install. Supported versions
937- are "9.1", "9.2", "9.3" & "9.4". The default version for the
938- deployed Ubuntu release is used when the version is not specified.
939- extra_pg_conf:
940- # The defaults here match the defaults chosen by the charm,
941- # so removing them will not change them. They are listed
942- # as documentation. The charm actually loads the non-calculated
943- # defaults from this config.yaml file to make it unlikely it will
944- # get out of sync with reality.
945- default: |
946- # Additional service specific postgresql.conf settings.
947- listen_addresses='*'
948- ssl=true
949- log_timezone=UTC
950- log_checkpoints=true
951- log_connections=true
952- log_disconnections=true
953- log_autovacuum_min_duration=-1
954- log_line_prefix='%t [%p]: [%l-1] db=%d,user=%u '
955- archive_mode=true
956- archive_command='/bin/true'
957- hot_standby=true
958- max_wal_senders=80
959- # max_wal_senders=num_units * 2 + 5
960- # wal_level=hot_standby (<9.4) or logical (>=9.4)
961- # shared_buffers=total_ram*0.25
962- # effective_cache_size=total_ram*0.75
963- default_statistics_target=250
964- from_collapse_limit=16
965- join_collapse_limit=16
966- wal_buffers=-1
967- checkpoint_completion_target=0.9
968- password_encryption=true
969- max_connections=100
970- type: string
971- description: >
972- postgresql.conf settings, one per line in standard key=value
973- PostgreSQL format. These settings will generally override
974- any values selected by the charm. The charm however will
975- attempt to ensure minimum requirements for the charm's
976- operation are met.
977- extra_pg_auth:
978- type: string
979- default: ""
980- description: >
981- A comma separated extra pg_hba.conf auth rules.
982- This will be written to the pg_hba.conf file, one line per rule.
983- Note that this should not be needed as db relations already create
984- those rules the right way. Only use this if you really need too
985- (e.g. on a development environment), or are connecting juju managed
986- databases to external managed systems, or configuring replication
987- between unrelated PostgreSQL services using the manual_replication
988- option.
989- performance_tuning:
990- default: "Mixed"
991- type: string
992- description: >
993- DEPRECATED AND IGNORED. The pgtune project has been abandoned
994- and the packages dropped from Debian and Ubuntu. The charm
995- still performs some basic tuning, which users can tweak using
996- extra_pg_config.
997- manual_replication:
998- type: boolean
999- default: False
1000- description: >
1001- Enable or disable charm managed replication. When manual_replication
1002- is True, the operator is responsible for maintaining recovery.conf
1003- and performing any necessary database mirroring. The charm will
1004- still advertise the unit as standalone, master or hot standby to
1005- relations based on whether the system is in recovery mode or not.
1006- Note that this option makes it possible to create a PostgreSQL
1007- service with multiple master units, which is a very silly thing
1008- to do unless you are also using multi-master software like BDR.
1009- backup_schedule:
1010- default: "13 4 * * *"
1011- type: string
1012- description: Cron-formatted schedule for regular database backups.
1013- backup_retention_count:
1014- default: 7
1015- type: int
1016- description: Number of backups to retain.
1017- nagios_context:
1018- default: "juju"
1019- type: string
1020- description: >
1021- Used by the nrpe subordinate charms.
1022- A string that will be prepended to instance name to set the host name
1023- in nagios. So for instance the hostname would be something like:
1024- juju-postgresql-0
1025- If you're running multiple environments with the same services in them
1026- this allows you to differentiate between them.
1027- nagios_servicegroups:
1028- default: ""
1029- type: string
1030- description: >
1031- A comma-separated list of nagios servicegroups.
1032- If left empty, the nagios_context will be used as the servicegroup
1033- pgdg:
1034- description: >
1035- Enable the PostgreSQL Global Development Group APT repository
1036- (https://wiki.postgresql.org/wiki/Apt). This package source provides
1037- official PostgreSQL packages for Ubuntu LTS releases beyond those
1038- provided by the main Ubuntu archive.
1039- type: boolean
1040- default: false
1041- install_sources:
1042- description: >
1043- List of extra package sources, per charm-helpers standard.
1044- YAML format.
1045- type: string
1046- default: ""
1047- install_keys:
1048- description: >
1049- List of signing keys for install_sources package sources, per
1050- charmhelpers standard. YAML format.
1051- type: string
1052- default: ""
1053- wal_e_storage_uri:
1054- type: string
1055- default: ""
1056- description: |
1057+"options":
1058+ "extra_packages":
1059+ "description": "Space separated list of extra deb packages to install.\n"
1060+ "type": "string"
1061+ "default": ""
1062+ "package_status":
1063+ "default": "install"
1064+ "type": "string"
1065+ "description": "The status of service-affecting packages will be set to this value\
1066+ \ in the dpkg database. Valid values are \"install\" and \"hold\".\n"
1067+ "install_sources":
1068+ "description": "List of extra apt sources, per charm-helpers standard format (a\
1069+ \ yaml list of strings encoded as a string). Each source may be either a line\
1070+ \ that can be added directly to sources.list(5), or in the form ppa:<user>/<ppa-name>\
1071+ \ for adding Personal Package Archives, or a distribution component to enable.\n"
1072+ "type": "string"
1073+ "default": ""
1074+ "install_keys":
1075+ "description": "List of signing keys for install_sources package sources, per\
1076+ \ charmhelpers standard format (a yaml list of strings encoded as a string).\
1077+ \ The keys should be the full ASCII armoured GPG public keys. While GPG key\
1078+ \ ids are also supported and looked up on a keyserver, operators should be aware\
1079+ \ that this mechanism is insecure. null can be used if a standard package signing\
1080+ \ key is used that will already be installed on the machine, and for PPA sources\
1081+ \ where the package signing key is securely retrieved from Launchpad.\n"
1082+ "type": "string"
1083+ "default": ""
1084+ "admin_addresses":
1085+ "default": ""
1086+ "type": "string"
1087+ "description": "A comma-separated list of IP Addresses (or single IP) admin tools\
1088+ \ like pgAdmin3 will connect from. The IP addresses added here will be included\
1089+ \ in the pg_hba.conf file allowing ip connections to all databases on the server\
1090+ \ from the given IP addresses using md5 password encryption. IP address ranges\
1091+ \ are also supported, using the standard format described in the PostgreSQL\
1092+ \ reference guide.\n"
1093+ "locale":
1094+ "default": "C"
1095+ "type": "string"
1096+ "description": "Locale of service, defining language, default collation order,\
1097+ \ and default formatting of numbers, currency, dates & times. Can only be set\
1098+ \ when deploying the first unit of a service.\n"
1099+ "encoding":
1100+ "default": "UTF-8"
1101+ "type": "string"
1102+ "description": "Default encoding used to store text in this service. Can only\
1103+ \ be set when deploying the first unit of a service.\n"
1104+ "relation_database_privileges":
1105+ "default": "ALL"
1106+ "type": "string"
1107+ "description": "A comma-separated list of database privileges to grant to relation\
1108+ \ users on their databases. The defaults allow to connect to the database (CONNECT),\
1109+ \ create objects such as tables (CREATE), and create temporary tables (TEMPORARY).\
1110+ \ Client charms that create objects in the database are responsible to granting\
1111+ \ suitable access on those objects to other roles and users (or PUBLIC) using\
1112+ \ standard GRANT statements.\n"
1113+ "version":
1114+ "default": ""
1115+ "type": "string"
1116+ "description": "Version of PostgreSQL that we want to install. Supported versions\
1117+ \ are \"9.1\", \"9.2\", \"9.3\", \"9.4\" & \"9.5\". The default version for\
1118+ \ the deployed Ubuntu release is used when the version is unspecified.\n"
1119+ "extra_pg_conf":
1120+ "default": |
1121+ # Additional service specific postgresql.conf settings.
1122+ listen_addresses='*'
1123+ ssl=true
1124+ log_timezone=UTC
1125+ log_checkpoints=true
1126+ log_connections=true
1127+ log_disconnections=true
1128+ log_autovacuum_min_duration=-1
1129+ log_line_prefix='%t [%p]: [%l-1] db=%d,user=%u '
1130+ archive_mode=on
1131+ archive_command='/bin/true'
1132+ hot_standby=true
1133+ max_wal_senders=80
1134+ # max_wal_senders=num_units * 2 + 5
1135+ # wal_level=hot_standby (<9.4) or logical (>=9.4)
1136+ # shared_buffers=total_ram*0.25
1137+ # effective_cache_size=total_ram*0.75
1138+ default_statistics_target=250
1139+ from_collapse_limit=16
1140+ join_collapse_limit=16
1141+ wal_buffers=-1
1142+ checkpoint_completion_target=0.9
1143+ password_encryption=true
1144+ max_connections=100
1145+ "type": "string"
1146+ "description": "postgresql.conf settings, one per line in standard key=value PostgreSQL\
1147+ \ format. These settings will generally override any values selected by the\
1148+ \ charm. The charm however will attempt to ensure minimum requirements for the\
1149+ \ charm's operation are met.\n"
1150+ "extra_pg_auth":
1151+ "type": "string"
1152+ "default": ""
1153+ "description": "A comma separated extra pg_hba.conf auth rules. This will be written\
1154+ \ to the pg_hba.conf file, one line per rule. Note that this should not be needed\
1155+ \ as db relations already create those rules the right way. Only use this if\
1156+ \ you really need too (e.g. on a development environment), or are connecting\
1157+ \ juju managed databases to external managed systems, or configuring replication\
1158+ \ between unrelated PostgreSQL services using the manual_replication option.\n"
1159+ "idle_reap_secs":
1160+ "type": "int"
1161+ "default": !!int "900"
1162+ "description": "Terminate transactions that have been idle more than this many\
1163+ \ seconds. While this may seem harsh, in most environments it is preferable\
1164+ \ to allowing them to create database bloat and hold locks needed by well behaved\
1165+ \ transactions. Set to 0 to disable.\n"
1166+ "performance_tuning":
1167+ "default": "Mixed"
1168+ "type": "string"
1169+ "description": "DEPRECATED AND IGNORED. The pgtune project has been abandoned\
1170+ \ and the packages dropped from Debian and Ubuntu. The charm still performs\
1171+ \ some basic tuning, which users can tweak using extra_pg_config.\n"
1172+ "manual_replication":
1173+ "type": "boolean"
1174+ "default": !!bool "false"
1175+ "description": "Enable or disable charm managed replication. When manual_replication\
1176+ \ is True, the operator is responsible for maintaining recovery.conf and performing\
1177+ \ any necessary database mirroring. The charm will still advertise the unit\
1178+ \ as standalone, master or hot standby to relations based on whether the system\
1179+ \ is in recovery mode or not. Note that this option makes it possible to create\
1180+ \ a PostgreSQL service with multiple master units, which is a very silly thing\
1181+ \ to do unless you are also using multi-master software like BDR.\n"
1182+ "backup_schedule":
1183+ "default": "13 4 * * *"
1184+ "type": "string"
1185+ "description": "Cron-formatted schedule for regular database backups."
1186+ "backup_retention_count":
1187+ "default": !!int "7"
1188+ "type": "int"
1189+ "description": "Number of backups to retain."
1190+ "backup_dir":
1191+ "default": "/var/lib/postgresql/backups"
1192+ "type": "string"
1193+ "description": "Directory to place backups in.\n"
1194+ "nagios_context":
1195+ "default": "juju"
1196+ "type": "string"
1197+ "description": "Used by the nrpe subordinate charms. A string that will be prepended\
1198+ \ to instance name to set the host name in nagios. So for instance the hostname\
1199+ \ would be something like:\n juju-postgresql-0\nIf you're running multiple\
1200+ \ environments with the same services in them this allows you to differentiate\
1201+ \ between them.\n"
1202+ "nagios_servicegroups":
1203+ "default": ""
1204+ "type": "string"
1205+ "description": "A comma-separated list of nagios servicegroups. If left empty,\
1206+ \ the nagios_context will be used as the servicegroup\n"
1207+ "pgdg":
1208+ "description": "Enable the PostgreSQL Global Development Group APT repository\
1209+ \ (https://wiki.postgresql.org/wiki/Apt). This package source provides official\
1210+ \ PostgreSQL packages for Ubuntu LTS releases beyond those provided by the main\
1211+ \ Ubuntu archive.\n"
1212+ "type": "boolean"
1213+ "default": !!bool "false"
1214+ "wal_e_storage_uri":
1215+ "type": "string"
1216+ "default": ""
1217+ "description": |
1218 EXPERIMENTAL.
1219 Specify storage to be used by WAL-E. Every PostgreSQL service must use
1220 a unique URI. Backups will be unrecoverable if it is not unique. The
1221@@ -181,437 +172,343 @@
1222 storage. Point-in-time recovery becomes possible, as is disabling the
1223 streaming_replication configuration item and relying solely on
1224 log shipping for replication.
1225- wal_e_backup_schedule:
1226- type: string
1227- default: "13 0 * * *"
1228- description: >
1229- EXPERIMENTAL.
1230- Cron-formatted schedule for WAL-E database backups. If
1231- wal_e_backup_schedule is unset, WAL files will never be removed from
1232- WAL-E storage.
1233- wal_e_backup_retention:
1234- type: int
1235- default: 2
1236- description: >
1237- EXPERIMENTAL.
1238- Number of recent base backups and WAL files to retain.
1239- You need enough space for this many backups plus one more, as
1240- an old backup will only be removed after a new one has been
1241- successfully made to replace it.
1242- streaming_replication:
1243- type: boolean
1244- default: true
1245- description: >
1246- Enable streaming replication. Normally, streaming replication is
1247- always used, and any log shipping configured is used as a fallback.
1248- Turning this off without configuring log shipping is an error.
1249- os_username:
1250- type: string
1251- default: ""
1252- description: EXPERIMENTAL. OpenStack Swift username.
1253- os_password:
1254- type: string
1255- default: ""
1256- description: EXPERIMENTAL. OpenStack Swift password.
1257- os_auth_url:
1258- type: string
1259- default: ""
1260- description: EXPERIMENTAL. OpenStack Swift authentication URL.
1261- os_tenant_name:
1262- type: string
1263- default: ""
1264- description: EXPERIMENTAL. OpenStack Swift tenant name.
1265- aws_access_key_id:
1266- type: string
1267- default: ""
1268- description: EXPERIMENTAL. Amazon AWS access key id.
1269- aws_secret_access_key:
1270- type: string
1271- default: ""
1272- description: EXPERIMENTAL. Amazon AWS secret access key.
1273- wabs_account_name:
1274- type: string
1275- default: ""
1276- description: EXPERIMENTAL. Windows Azure account name.
1277- wabs_access_key:
1278- type: string
1279- default: ""
1280- description: EXPERIMENTAL. Windows Azure access key.
1281- package_status:
1282- default: "install"
1283- type: string
1284- description: >
1285- The status of service-affecting packages will be set to this
1286- value in the dpkg database. Useful valid values are "install"
1287- and "hold".
1288- # statsd-compatible metrics
1289- metrics_target:
1290- default: ""
1291- type: string
1292- description: >
1293- Destination for statsd-format metrics, format "host:port". If
1294- not present and valid, metrics disabled.
1295- metrics_prefix:
1296- default: "dev.$UNIT.postgresql"
1297- type: string
1298- description: >
1299- Prefix for metrics. Special value $UNIT can be used to include the
1300- name of the unit in the prefix.
1301- metrics_sample_interval:
1302- default: 5
1303- type: int
1304- description: Period for metrics cron job to run in minutes
1305-
1306-
1307- # DEPRECATED SETTINGS.
1308- # Remove them one day. They remain here to avoid making existing
1309- # configurations fail.
1310- advisory_lock_restart_key:
1311- default: 765
1312- type: int
1313- description: >
1314- DEPRECATED AND IGNORED.
1315- An advisory lock key used internally by the charm. You do not need
1316- to change it unless it happens to conflict with an advisory lock key
1317- being used by your applications.
1318- extra-packages:
1319- default: ""
1320- type: string
1321- description: DEPRECATED. Use extra_packages.
1322- listen_port:
1323- default: -1
1324- type: int
1325- description: >
1326- DEPRECATED. Use extra_pg_conf.
1327- Port to listen on. Default is automatically assigned.
1328- max_connections:
1329- default: 100
1330- type: int
1331- description: >
1332- DEPRECATED. Use extra_pg_conf.
1333- Maximum number of connections to allow to the PG database
1334- max_prepared_transactions:
1335- default: 0
1336- type: int
1337- description: >
1338- DEPRECATED. Use extra_pg_conf.
1339- Maximum number of prepared two phase commit transactions, waiting
1340- to be committed. Defaults to 0. as using two phase commit without
1341- a process to monitor and resolve lost transactions is dangerous.
1342- ssl:
1343- default: "True"
1344- type: string
1345- description: >
1346- DEPRECATED. Use extra_pg_conf.
1347- Whether PostgreSQL should talk SSL
1348- log_min_duration_statement:
1349- default: -1
1350- type: int
1351- description: >
1352- DEPRECATED. Use extra_pg_conf.
1353- -1 is disabled, 0 logs all statements
1354- and their durations, > 0 logs only
1355- statements running at least this number
1356- of milliseconds
1357- log_checkpoints:
1358- default: False
1359- type: boolean
1360- description: >
1361- DEPRECATED. Use extra_pg_conf.
1362- log_connections:
1363- default: False
1364- type: boolean
1365- description: DEPRECATED. Use extra_pg_conf.
1366- log_disconnections:
1367- default: False
1368- type: boolean
1369- description: DEPRECATED. Use extra_pg_conf.
1370- log_temp_files:
1371- default: "-1"
1372- type: string
1373- description: >
1374- DEPRECATED. Use extra_pg_conf.
1375- Log creation of temporary files larger than the threshold.
1376- -1 disables the feature, 0 logs all temporary files, or specify
1377- the threshold size with an optional unit (eg. "512KB", default
1378- unit is kilobytes).
1379- log_line_prefix:
1380- default: "%t [%p]: [%l-1] db=%d,user=%u "
1381- type: string
1382- description: |
1383- DEPRECATED. Use extra_pg_conf.
1384- special values:
1385- %a = application name
1386- %u = user name
1387- %d = database name
1388- %r = remote host and port
1389- %h = remote host
1390- %p = process ID
1391- %t = timestamp without milliseconds
1392- %m = timestamp with milliseconds
1393- %i = command tag
1394- %e = SQL state
1395- %c = session ID
1396- %l = session line number
1397- %s = session start timestamp
1398- %v = virtual transaction ID
1399- %x = transaction ID (0 if none)
1400- %q = stop here in non-session processes
1401- %% = '%'
1402- e.g. '<%u%%%d> '
1403- log_lock_waits:
1404- default: False
1405- type: boolean
1406- description: DEPRECATED. Use extra_pg_conf.
1407- log_timezone:
1408- default: "UTC"
1409- type: string
1410- description: DEPRECATED. Use extra_pg_conf.
1411- autovacuum:
1412- default: True
1413- type: boolean
1414- description: >
1415- DEPRECATED. Use extra_pg_conf.
1416- Autovacuum should almost always be running. If you want to turn this
1417- off, you are probably following out of date documentation.
1418- log_autovacuum_min_duration:
1419- default: -1
1420- type: int
1421- description: >
1422- DEPRECATED. Use extra_pg_conf.
1423- -1 disables, 0 logs all actions and their durations, > 0 logs only
1424- actions running at least this number of milliseconds.
1425- autovacuum_analyze_threshold:
1426- default: 50
1427- type: int
1428- description: >
1429- DEPRECATED. Use extra_pg_conf.
1430- min number of row updates before analyze
1431- autovacuum_vacuum_scale_factor:
1432- default: 0.2
1433- type: float
1434- description: >
1435- DEPRECATED. Use extra_pg_conf.
1436- Fraction of table size before vacuum
1437- autovacuum_analyze_scale_factor:
1438- default: 0.1
1439- type: float
1440- description: >
1441- DEPRECATED. Use extra_pg_conf.
1442- Fraction of table size before analyze
1443- autovacuum_vacuum_cost_delay:
1444- default: "20ms"
1445- type: string
1446- description: >
1447- DEPRECATED. Use extra_pg_conf.
1448- Default vacuum cost delay for autovacuum, in milliseconds;
1449- -1 means use vacuum_cost_delay
1450- search_path:
1451- default: "\"$user\",public"
1452- type: string
1453- description: >
1454- DEPRECATED. Use extra_pg_conf.
1455- Comma separated list of schema names for
1456- the default SQL search path.
1457- standard_conforming_strings:
1458- default: True
1459- type: boolean
1460- description: >
1461- DEPRECATED. Use extra_pg_conf.
1462- Standard conforming strings
1463- hot_standby:
1464- default: False
1465- type: boolean
1466- description: >
1467- DEPRECATED. Use extra_pg_conf.
1468- Hot standby or warm standby. When True, queries can be run against
1469- the database when in recovery or standby mode (ie. replicated).
1470- Overridden when service contains multiple units.
1471- hot_standby_feedback:
1472- default: False
1473- type: boolean
1474- description: >
1475- DEPRECATED. Use extra_pg_conf.
1476- Hot standby feedback, informing a master about in progress
1477- transactions on a streaming hot standby and allowing the master to
1478- defer cleanup and avoid query cancelations on the hot standby.
1479- wal_level:
1480- default: minimal
1481- type: string
1482- description: >
1483- DEPRECATED. Use extra_pg_conf.
1484- 'minimal', 'archive', 'hot_standby' or 'logical'. Defines how much
1485- information is written to the WAL. Set to 'minimal' for stand alone
1486- databases and 'hot_standby' for replicated setups. Overridden by
1487- juju when replication is used.
1488- max_wal_senders:
1489- default: 0
1490- type: int
1491- description: >
1492- DEPRECATED. Use extra_pg_conf.
1493- Maximum number of hot standbys that can connect using
1494- streaming replication. Set this to the expected maximum number of
1495- hot standby units to avoid unnecessary blocking and database restarts.
1496- Overridden by juju if necessary.
1497- wal_keep_segments:
1498- default: 0
1499- type: int
1500- description: >
1501- DEPRECATED. Use extra_pg_conf.
1502- Number of old WAL files to keep, providing a larger buffer for
1503- streaming hot standbys to catch up from when lagged. Each WAL file
1504- is 16MB in size. The WAL files are the buffer of how far a
1505- hot standby can lag behind the master, and replication fails if
1506- this buffer is overrun. When this service is replicated, the larger
1507- value of wal_keep_segments and replicated_wal_keep_segments is used.
1508- replicated_wal_keep_segments:
1509- default: 5000
1510- type: int
1511- description: >
1512- DEPRECATED. Use extra_pg_conf.
1513- Value of wal_keep_segments used when this service is replicated.
1514- This setting only exists to provide a sane default when replication
1515- is requested (so it doesn't fail) and nobody bothered to change the
1516- wal_keep_segments setting.
1517- archive_mode:
1518- default: False
1519- type: boolean
1520- description: >
1521- DEPRECATED. Use extra_pg_conf.
1522- Enable archiving of WAL files using the command specified by
1523- archive_command. If archive_mode is enabled and archive_command not
1524- set, then archiving is deferred until archive_command is set and the
1525- WAL files will accumulate.
1526- archive_command:
1527- default: ""
1528- type: string
1529- description: >
1530- DEPRECATED. Use extra_pg_conf.
1531- Command used to archive WAL files when archive_mode is set and
1532- wal_level > minimal.
1533- work_mem:
1534- default: "1MB"
1535- type: string
1536- description: >
1537- DEPRECATED. Use extra_pg_conf. Working Memory.
1538- Ignored unless 'performance_tuning' is set to 'manual'.
1539- maintenance_work_mem:
1540- default: "1MB"
1541- type: string
1542- description: >
1543- DEPRECATED. Use extra_pg_conf. Maintenance working memory.
1544- Ignored unless 'performance_tuning' is set to 'manual'.
1545- kernel_shmall:
1546- default: 0
1547- type: int
1548- description: >
1549- DEPRECATED and ignored.
1550- Total amount of shared memory available, in bytes.
1551- kernel_shmmax:
1552- default: 0
1553- type: int
1554- description: >
1555- DEPRECATED and ignored.
1556- The maximum size, in bytes, of a shared memory segment.
1557- shared_buffers:
1558- default: ""
1559- type: string
1560- description: >
1561- DEPRECATED. Use extra_pg_conf.
1562- The amount of memory the database server uses for shared memory
1563- buffers. This string should be of the format '###MB'.
1564- Ignored unless 'performance_tuning' is set to 'manual'.
1565- effective_cache_size:
1566- default: ""
1567- type: string
1568- description: >
1569- DEPRECATED. Use extra_pg_conf.
1570- Effective cache size is an estimate of how much memory is available for
1571- disk caching within the database. (50% to 75% of system memory). This
1572- string should be of the format '###MB'. Ignored unless
1573- 'performance_tuning' is set to 'manual'.
1574- default_statistics_target:
1575- default: -1
1576- type: int
1577- description: >
1578- DEPRECATED. Use extra_pg_conf.
1579- Sets the default statistics target for table columns without a
1580- column-specific target set via ALTER TABLE SET STATISTICS.
1581- Leave unchanged to use the server default, which in recent
1582- releases is 100. Ignored unless 'performance_tuning' is 'manual'.
1583- Larger values increase the time needed to do ANALYZE, but
1584- might improve the quality of the planner's estimates.
1585- collapse_limit:
1586- default: -1
1587- type: int
1588- description: >
1589- DEPRECATED. Use extra_pg_conf.
1590- Sets the from_collapse_limit and join_collapse_limit query planner
1591- options, controlling the maximum number of tables that can be joined
1592- before the turns off the table collapse query optimization.
1593- temp_buffers:
1594- default: "1MB"
1595- type: string
1596- description: >
1597- DEPRECATED. Use extra_pg_conf.
1598- The maximum number of temporary buffers used by each database session.
1599- wal_buffers:
1600- default: "-1"
1601- type: string
1602- description: >
1603- DEPRECATED. Use extra_pg_conf.
1604- min 32kB, -1 sets based on shared_buffers (change requires restart).
1605- Ignored unless 'performance_tuning' is set to 'manual'.
1606- checkpoint_segments:
1607- default: 10
1608- type: int
1609- description: >
1610- DEPRECATED. Use extra_pg_conf.
1611- in logfile segments, min 1, 16MB each.
1612- Ignored unless 'performance_tuning' is set to 'manual'.
1613- checkpoint_completion_target:
1614- default: 0.9
1615- type: float
1616- description: >
1617- DEPRECATED. Use extra_pg_conf.
1618- checkpoint target duration time, as a fraction of checkpoint_timeout.
1619- Range [0.0, 1.0].
1620- checkpoint_timeout:
1621- default: ""
1622- type: string
1623- description: >
1624- DEPRECATED. Use extra_pg_conf.
1625- Maximum time between automatic WAL checkpoints. range '30s-1h'.
1626- If left empty, the default postgresql value will be used.
1627- fsync:
1628- type: boolean
1629- default: True
1630- description: >
1631- DEPRECATED. Use extra_pg_conf.
1632- Turns forced synchronization on/off. If fsync is turned off, database
1633- failures are likely to involve database corruption and require
1634- recreating the unit
1635- synchronous_commit:
1636- type: boolean
1637- default: True
1638- description: >
1639- DEPRECATED. Use extra_pg_conf.
1640- Immediate fsync after commit.
1641- full_page_writes:
1642- type: boolean
1643- default: True
1644- description: >
1645- DEPRECATED. Use extra_pg_conf.
1646- Recover from partial page writes.
1647- random_page_cost:
1648- default: 4.0
1649- type: float
1650- description: >
1651- DEPRECATED. Use extra_pg_conf. Random page cost
1652- backup_dir:
1653- default: "/var/lib/postgresql/backups"
1654- type: string
1655- description: >
1656- DEPRECATED. Directory to place backups in. If you change this,
1657- your backups will go to this path and not the 'backups' Juju
1658- storage mount.
1659+ "wal_e_backup_schedule":
1660+ "type": "string"
1661+ "default": "13 0 * * *"
1662+ "description": "EXPERIMENTAL. Cron-formatted schedule for WAL-E database backups.\
1663+ \ If wal_e_backup_schedule is unset, WAL files will never be removed from WAL-E\
1664+ \ storage.\n"
1665+ "wal_e_backup_retention":
1666+ "type": "int"
1667+ "default": !!int "2"
1668+ "description": "EXPERIMENTAL. Number of recent base backups and WAL files to retain.\
1669+ \ You need enough space for this many backups plus one more, as an old backup\
1670+ \ will only be removed after a new one has been successfully made to replace\
1671+ \ it.\n"
1672+ "streaming_replication":
1673+ "type": "boolean"
1674+ "default": !!bool "true"
1675+ "description": "Enable streaming replication. Normally, streaming replication\
1676+ \ is always used, and any log shipping configured is used as a fallback. Turning\
1677+ \ this off without configuring log shipping is an error.\n"
1678+ "os_username":
1679+ "type": "string"
1680+ "default": ""
1681+ "description": "EXPERIMENTAL. OpenStack Swift username."
1682+ "os_password":
1683+ "type": "string"
1684+ "default": ""
1685+ "description": "EXPERIMENTAL. OpenStack Swift password."
1686+ "os_auth_url":
1687+ "type": "string"
1688+ "default": ""
1689+ "description": "EXPERIMENTAL. OpenStack Swift authentication URL."
1690+ "os_tenant_name":
1691+ "type": "string"
1692+ "default": ""
1693+ "description": "EXPERIMENTAL. OpenStack Swift tenant name."
1694+ "aws_access_key_id":
1695+ "type": "string"
1696+ "default": ""
1697+ "description": "EXPERIMENTAL. Amazon AWS access key id."
1698+ "aws_secret_access_key":
1699+ "type": "string"
1700+ "default": ""
1701+ "description": "EXPERIMENTAL. Amazon AWS secret access key."
1702+ "wabs_account_name":
1703+ "type": "string"
1704+ "default": ""
1705+ "description": "EXPERIMENTAL. Windows Azure account name."
1706+ "wabs_access_key":
1707+ "type": "string"
1708+ "default": ""
1709+ "description": "EXPERIMENTAL. Windows Azure access key."
1710+ "metrics_target":
1711+ "default": ""
1712+ "type": "string"
1713+ "description": "Destination for statsd-format metrics, format \"host:port\". If\
1714+ \ not present and valid, metrics disabled.\n"
1715+ "metrics_prefix":
1716+ "default": "dev.$UNIT.postgresql"
1717+ "type": "string"
1718+ "description": "Prefix for metrics. Special value $UNIT can be used to include\
1719+ \ the name of the unit in the prefix.\n"
1720+ "metrics_sample_interval":
1721+ "default": !!int "5"
1722+ "type": "int"
1723+ "description": "Period for metrics cron job to run in minutes"
1724+ "advisory_lock_restart_key":
1725+ "default": !!int "765"
1726+ "type": "int"
1727+ "description": "DEPRECATED AND IGNORED. An advisory lock key used internally by\
1728+ \ the charm. You do not need to change it unless it happens to conflict with\
1729+ \ an advisory lock key being used by your applications.\n"
1730+ "extra-packages":
1731+ "default": ""
1732+ "type": "string"
1733+ "description": "DEPRECATED. Use extra_packages."
1734+ "listen_port":
1735+ "default": !!int "-1"
1736+ "type": "int"
1737+ "description": "DEPRECATED. Use extra_pg_conf. Port to listen on. Default is automatically\
1738+ \ assigned.\n"
1739+ "max_connections":
1740+ "default": !!int "100"
1741+ "type": "int"
1742+ "description": "DEPRECATED. Use extra_pg_conf. Maximum number of connections to\
1743+ \ allow to the PG database\n"
1744+ "max_prepared_transactions":
1745+ "default": !!int "0"
1746+ "type": "int"
1747+ "description": "DEPRECATED. Use extra_pg_conf. Maximum number of prepared two\
1748+ \ phase commit transactions, waiting to be committed. Defaults to 0. as using\
1749+ \ two phase commit without a process to monitor and resolve lost transactions\
1750+ \ is dangerous.\n"
1751+ "ssl":
1752+ "default": "True"
1753+ "type": "string"
1754+ "description": "DEPRECATED. Use extra_pg_conf. Whether PostgreSQL should talk\
1755+ \ SSL\n"
1756+ "log_min_duration_statement":
1757+ "default": !!int "-1"
1758+ "type": "int"
1759+ "description": "DEPRECATED. Use extra_pg_conf. -1 is disabled, 0 logs all statements\
1760+ \ and their durations, > 0 logs only statements running at least this number\
1761+ \ of milliseconds\n"
1762+ "log_checkpoints":
1763+ "default": !!bool "false"
1764+ "type": "boolean"
1765+ "description": "DEPRECATED. Use extra_pg_conf.\n"
1766+ "log_connections":
1767+ "default": !!bool "false"
1768+ "type": "boolean"
1769+ "description": "DEPRECATED. Use extra_pg_conf."
1770+ "log_disconnections":
1771+ "default": !!bool "false"
1772+ "type": "boolean"
1773+ "description": "DEPRECATED. Use extra_pg_conf."
1774+ "log_temp_files":
1775+ "default": "-1"
1776+ "type": "string"
1777+ "description": "DEPRECATED. Use extra_pg_conf. Log creation of temporary files\
1778+ \ larger than the threshold. -1 disables the feature, 0 logs all temporary files,\
1779+ \ or specify the threshold size with an optional unit (eg. \"512KB\", default\
1780+ \ unit is kilobytes).\n"
1781+ "log_line_prefix":
1782+ "default": "%t [%p]: [%l-1] db=%d,user=%u "
1783+ "type": "string"
1784+ "description": |
1785+ DEPRECATED. Use extra_pg_conf.
1786+ special values:
1787+ %a = application name
1788+ %u = user name
1789+ %d = database name
1790+ %r = remote host and port
1791+ %h = remote host
1792+ %p = process ID
1793+ %t = timestamp without milliseconds
1794+ %m = timestamp with milliseconds
1795+ %i = command tag
1796+ %e = SQL state
1797+ %c = session ID
1798+ %l = session line number
1799+ %s = session start timestamp
1800+ %v = virtual transaction ID
1801+ %x = transaction ID (0 if none)
1802+ %q = stop here in non-session processes
1803+ %% = '%'
1804+ e.g. '<%u%%%d> '
1805+ "log_lock_waits":
1806+ "default": !!bool "false"
1807+ "type": "boolean"
1808+ "description": "DEPRECATED. Use extra_pg_conf."
1809+ "log_timezone":
1810+ "default": "UTC"
1811+ "type": "string"
1812+ "description": "DEPRECATED. Use extra_pg_conf."
1813+ "autovacuum":
1814+ "default": !!bool "true"
1815+ "type": "boolean"
1816+ "description": "DEPRECATED. Use extra_pg_conf. Autovacuum should almost always\
1817+ \ be running. If you want to turn this off, you are probably following out of\
1818+ \ date documentation.\n"
1819+ "log_autovacuum_min_duration":
1820+ "default": !!int "-1"
1821+ "type": "int"
1822+ "description": "DEPRECATED. Use extra_pg_conf. -1 disables, 0 logs all actions\
1823+ \ and their durations, > 0 logs only actions running at least this number of\
1824+ \ milliseconds.\n"
1825+ "autovacuum_analyze_threshold":
1826+ "default": !!int "50"
1827+ "type": "int"
1828+ "description": "DEPRECATED. Use extra_pg_conf. min number of row updates before\
1829+ \ analyze\n"
1830+ "autovacuum_vacuum_scale_factor":
1831+ "default": !!float "0.2"
1832+ "type": "float"
1833+ "description": "DEPRECATED. Use extra_pg_conf. Fraction of table size before vacuum\n"
1834+ "autovacuum_analyze_scale_factor":
1835+ "default": !!float "0.1"
1836+ "type": "float"
1837+ "description": "DEPRECATED. Use extra_pg_conf. Fraction of table size before analyze\n"
1838+ "autovacuum_vacuum_cost_delay":
1839+ "default": "20ms"
1840+ "type": "string"
1841+ "description": "DEPRECATED. Use extra_pg_conf. Default vacuum cost delay for autovacuum,\
1842+ \ in milliseconds; -1 means use vacuum_cost_delay\n"
1843+ "search_path":
1844+ "default": "\"$user\",public"
1845+ "type": "string"
1846+ "description": "DEPRECATED. Use extra_pg_conf. Comma separated list of schema\
1847+ \ names for the default SQL search path.\n"
1848+ "standard_conforming_strings":
1849+ "default": !!bool "true"
1850+ "type": "boolean"
1851+ "description": "DEPRECATED. Use extra_pg_conf. Standard conforming strings\n"
1852+ "hot_standby":
1853+ "default": !!bool "false"
1854+ "type": "boolean"
1855+ "description": "DEPRECATED. Use extra_pg_conf. Hot standby or warm standby. When\
1856+ \ True, queries can be run against the database when in recovery or standby\
1857+ \ mode (ie. replicated). Overridden when service contains multiple units.\n"
1858+ "hot_standby_feedback":
1859+ "default": !!bool "false"
1860+ "type": "boolean"
1861+ "description": "DEPRECATED. Use extra_pg_conf. Hot standby feedback, informing\
1862+ \ a master about in progress transactions on a streaming hot standby and allowing\
1863+ \ the master to defer cleanup and avoid query cancelations on the hot standby.\n"
1864+ "wal_level":
1865+ "default": "minimal"
1866+ "type": "string"
1867+ "description": "DEPRECATED. Use extra_pg_conf. 'minimal', 'archive', 'hot_standby'\
1868+ \ or 'logical'. Defines how much information is written to the WAL. Set to 'minimal'\
1869+ \ for stand alone databases and 'hot_standby' for replicated setups. Overridden\
1870+ \ by juju when replication is used.\n"
1871+ "max_wal_senders":
1872+ "default": !!int "0"
1873+ "type": "int"
1874+ "description": "DEPRECATED. Use extra_pg_conf. Maximum number of hot standbys\
1875+ \ that can connect using streaming replication. Set this to the expected maximum\
1876+ \ number of hot standby units to avoid unnecessary blocking and database restarts.\
1877+ \ Overridden by juju if necessary.\n"
1878+ "wal_keep_segments":
1879+ "default": !!int "0"
1880+ "type": "int"
1881+ "description": "DEPRECATED. Use extra_pg_conf. Number of old WAL files to keep,\
1882+ \ providing a larger buffer for streaming hot standbys to catch up from when\
1883+ \ lagged. Each WAL file is 16MB in size. The WAL files are the buffer of how\
1884+ \ far a hot standby can lag behind the master, and replication fails if this\
1885+ \ buffer is overrun. When this service is replicated, the larger value of wal_keep_segments\
1886+ \ and replicated_wal_keep_segments is used.\n"
1887+ "replicated_wal_keep_segments":
1888+ "default": !!int "5000"
1889+ "type": "int"
1890+ "description": "DEPRECATED. Use extra_pg_conf. Value of wal_keep_segments used\
1891+ \ when this service is replicated. This setting only exists to provide a sane\
1892+ \ default when replication is requested (so it doesn't fail) and nobody bothered\
1893+ \ to change the wal_keep_segments setting.\n"
1894+ "archive_mode":
1895+ "default": !!bool "false"
1896+ "type": "boolean"
1897+ "description": "DEPRECATED. Use extra_pg_conf. Enable archiving of WAL files using\
1898+ \ the command specified by archive_command. If archive_mode is enabled and archive_command\
1899+ \ not set, then archiving is deferred until archive_command is set and the WAL\
1900+ \ files will accumulate.\n"
1901+ "archive_command":
1902+ "default": ""
1903+ "type": "string"
1904+ "description": "DEPRECATED. Use extra_pg_conf. Command used to archive WAL files\
1905+ \ when archive_mode is set and wal_level > minimal.\n"
1906+ "work_mem":
1907+ "default": "1MB"
1908+ "type": "string"
1909+ "description": "DEPRECATED. Use extra_pg_conf. Working Memory. Ignored unless\
1910+ \ 'performance_tuning' is set to 'manual'.\n"
1911+ "maintenance_work_mem":
1912+ "default": "1MB"
1913+ "type": "string"
1914+ "description": "DEPRECATED. Use extra_pg_conf. Maintenance working memory. Ignored\
1915+ \ unless 'performance_tuning' is set to 'manual'.\n"
1916+ "kernel_shmall":
1917+ "default": !!int "0"
1918+ "type": "int"
1919+ "description": "DEPRECATED and ignored. Total amount of shared memory available,\
1920+ \ in bytes.\n"
1921+ "kernel_shmmax":
1922+ "default": !!int "0"
1923+ "type": "int"
1924+ "description": "DEPRECATED and ignored. The maximum size, in bytes, of a shared\
1925+ \ memory segment.\n"
1926+ "shared_buffers":
1927+ "default": ""
1928+ "type": "string"
1929+ "description": "DEPRECATED. Use extra_pg_conf. The amount of memory the database\
1930+ \ server uses for shared memory buffers. This string should be of the format\
1931+ \ '###MB'. Ignored unless 'performance_tuning' is set to 'manual'.\n"
1932+ "effective_cache_size":
1933+ "default": ""
1934+ "type": "string"
1935+ "description": "DEPRECATED. Use extra_pg_conf. Effective cache size is an estimate\
1936+ \ of how much memory is available for disk caching within the database. (50%\
1937+ \ to 75% of system memory). This string should be of the format '###MB'. Ignored\
1938+ \ unless 'performance_tuning' is set to 'manual'.\n"
1939+ "default_statistics_target":
1940+ "default": !!int "-1"
1941+ "type": "int"
1942+ "description": "DEPRECATED. Use extra_pg_conf. Sets the default statistics target\
1943+ \ for table columns without a column-specific target set via ALTER TABLE SET\
1944+ \ STATISTICS. Leave unchanged to use the server default, which in recent releases\
1945+ \ is 100. Ignored unless 'performance_tuning' is 'manual'. Larger values increase\
1946+ \ the time needed to do ANALYZE, but might improve the quality of the planner's\
1947+ \ estimates.\n"
1948+ "collapse_limit":
1949+ "default": !!int "-1"
1950+ "type": "int"
1951+ "description": "DEPRECATED. Use extra_pg_conf. Sets the from_collapse_limit and\
1952+ \ join_collapse_limit query planner options, controlling the maximum number\
1953+ \ of tables that can be joined before the turns off the table collapse query\
1954+ \ optimization.\n"
1955+ "temp_buffers":
1956+ "default": "1MB"
1957+ "type": "string"
1958+ "description": "DEPRECATED. Use extra_pg_conf. The maximum number of temporary\
1959+ \ buffers used by each database session.\n"
1960+ "wal_buffers":
1961+ "default": "-1"
1962+ "type": "string"
1963+ "description": "DEPRECATED. Use extra_pg_conf. min 32kB, -1 sets based on shared_buffers\
1964+ \ (change requires restart). Ignored unless 'performance_tuning' is set to 'manual'.\n"
1965+ "checkpoint_segments":
1966+ "default": !!int "10"
1967+ "type": "int"
1968+ "description": "DEPRECATED. Use extra_pg_conf. in logfile segments, min 1, 16MB\
1969+ \ each. Ignored unless 'performance_tuning' is set to 'manual'.\n"
1970+ "checkpoint_completion_target":
1971+ "default": !!float "0.9"
1972+ "type": "float"
1973+ "description": "DEPRECATED. Use extra_pg_conf. checkpoint target duration time,\
1974+ \ as a fraction of checkpoint_timeout. Range [0.0, 1.0].\n"
1975+ "checkpoint_timeout":
1976+ "default": ""
1977+ "type": "string"
1978+ "description": "DEPRECATED. Use extra_pg_conf. Maximum time between automatic\
1979+ \ WAL checkpoints. range '30s-1h'. If left empty, the default postgresql value\
1980+ \ will be used.\n"
1981+ "fsync":
1982+ "type": "boolean"
1983+ "default": !!bool "true"
1984+ "description": "DEPRECATED. Use extra_pg_conf. Turns forced synchronization on/off.\
1985+ \ If fsync is turned off, database failures are likely to involve database corruption\
1986+ \ and require recreating the unit\n"
1987+ "synchronous_commit":
1988+ "type": "boolean"
1989+ "default": !!bool "true"
1990+ "description": "DEPRECATED. Use extra_pg_conf. Immediate fsync after commit.\n"
1991+ "full_page_writes":
1992+ "type": "boolean"
1993+ "default": !!bool "true"
1994+ "description": "DEPRECATED. Use extra_pg_conf. Recover from partial page writes.\n"
1995+ "random_page_cost":
1996+ "default": !!float "4.0"
1997+ "type": "float"
1998+ "description": "DEPRECATED. Use extra_pg_conf. Random page cost\n"
1999
2000=== removed file 'hooks/bootstrap.py'
2001--- hooks/bootstrap.py 2015-08-13 07:19:45 +0000
2002+++ hooks/bootstrap.py 1970-01-01 00:00:00 +0000
2003@@ -1,57 +0,0 @@
2004-#!/usr/bin/python3
2005-
2006-# Copyright 2015 Canonical Ltd.
2007-#
2008-# This file is part of the PostgreSQL Charm for Juju.
2009-#
2010-# This program is free software: you can redistribute it and/or modify
2011-# it under the terms of the GNU General Public License version 3, as
2012-# published by the Free Software Foundation.
2013-#
2014-# This program is distributed in the hope that it will be useful, but
2015-# WITHOUT ANY WARRANTY; without even the implied warranties of
2016-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2017-# PURPOSE. See the GNU General Public License for more details.
2018-#
2019-# You should have received a copy of the GNU General Public License
2020-# along with this program. If not, see <http://www.gnu.org/licenses/>.
2021-
2022-from charmhelpers import fetch
2023-from charmhelpers.core import hookenv
2024-
2025-
2026-def bootstrap():
2027- try:
2028- import psycopg2 # NOQA: flake8
2029- import jinja2 # NOQA: flake8
2030- except ImportError:
2031- packages = ['python3-psycopg2', 'python3-jinja2']
2032- fetch.apt_install(packages, fatal=True)
2033- import psycopg2 # NOQA: flake8
2034-
2035-
2036-def block_on_bad_juju():
2037- if not hookenv.has_juju_version('1.24'):
2038- hookenv.status_set('blocked', 'Requires Juju 1.24 or higher')
2039- # Error state, since we don't have 1.24 to give a nice blocked state.
2040- raise SystemExit(1)
2041-
2042-
2043-def upgrade_charm():
2044- block_on_bad_juju()
2045- # This needs to be imported after bootstrap() or required Python
2046- # packages may not have been installed.
2047- import upgrade
2048- upgrade.upgrade_charm()
2049-
2050-
2051-def default_hook():
2052- block_on_bad_juju()
2053- # This needs to be imported after bootstrap() or required Python
2054- # packages may not have been installed.
2055- import definitions
2056-
2057- hookenv.log('*** Start {!r} hook'.format(hookenv.hook_name()))
2058- sm = definitions.get_service_manager()
2059- sm.manage()
2060- hookenv.log('*** End {!r} hook'.format(hookenv.hook_name()))
2061
2062=== removed directory 'hooks/charmhelpers'
2063=== removed file 'hooks/charmhelpers/__init__.py'
2064--- hooks/charmhelpers/__init__.py 2015-06-26 10:00:19 +0000
2065+++ hooks/charmhelpers/__init__.py 1970-01-01 00:00:00 +0000
2066@@ -1,38 +0,0 @@
2067-# Copyright 2014-2015 Canonical Limited.
2068-#
2069-# This file is part of charm-helpers.
2070-#
2071-# charm-helpers is free software: you can redistribute it and/or modify
2072-# it under the terms of the GNU Lesser General Public License version 3 as
2073-# published by the Free Software Foundation.
2074-#
2075-# charm-helpers is distributed in the hope that it will be useful,
2076-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2077-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2078-# GNU Lesser General Public License for more details.
2079-#
2080-# You should have received a copy of the GNU Lesser General Public License
2081-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2082-
2083-# Bootstrap charm-helpers, installing its dependencies if necessary using
2084-# only standard libraries.
2085-import subprocess
2086-import sys
2087-
2088-try:
2089- import six # flake8: noqa
2090-except ImportError:
2091- if sys.version_info.major == 2:
2092- subprocess.check_call(['apt-get', 'install', '-y', 'python-six'])
2093- else:
2094- subprocess.check_call(['apt-get', 'install', '-y', 'python3-six'])
2095- import six # flake8: noqa
2096-
2097-try:
2098- import yaml # flake8: noqa
2099-except ImportError:
2100- if sys.version_info.major == 2:
2101- subprocess.check_call(['apt-get', 'install', '-y', 'python-yaml'])
2102- else:
2103- subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml'])
2104- import yaml # flake8: noqa
2105
2106=== removed file 'hooks/charmhelpers/context.py'
2107--- hooks/charmhelpers/context.py 2015-07-27 11:15:04 +0000
2108+++ hooks/charmhelpers/context.py 1970-01-01 00:00:00 +0000
2109@@ -1,206 +0,0 @@
2110-# Copyright 2015 Canonical Limited.
2111-#
2112-# This file is part of charm-helpers.
2113-#
2114-# charm-helpers is free software: you can redistribute it and/or modify
2115-# it under the terms of the GNU Lesser General Public License version 3 as
2116-# published by the Free Software Foundation.
2117-#
2118-# charm-helpers is distributed in the hope that it will be useful,
2119-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2120-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2121-# GNU Lesser General Public License for more details.
2122-#
2123-# You should have received a copy of the GNU Lesser General Public License
2124-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2125-'''
2126-A Pythonic API to interact with the charm hook environment.
2127-
2128-:author: Stuart Bishop <stuart.bishop@canonical.com>
2129-'''
2130-
2131-import six
2132-
2133-from charmhelpers.core import hookenv
2134-
2135-from collections import OrderedDict
2136-if six.PY3:
2137- from collections import UserDict # pragma: nocover
2138-else:
2139- from UserDict import IterableUserDict as UserDict # pragma: nocover
2140-
2141-
2142-class Relations(OrderedDict):
2143- '''Mapping relation name -> relation id -> Relation.
2144-
2145- >>> rels = Relations()
2146- >>> rels['sprog']['sprog:12']['client/6']['widget']
2147- 'remote widget'
2148- >>> rels['sprog']['sprog:12'].local['widget'] = 'local widget'
2149- >>> rels['sprog']['sprog:12'].local['widget']
2150- 'local widget'
2151- >>> rels.peer.local['widget']
2152- 'local widget on the peer relation'
2153- '''
2154- def __init__(self):
2155- super(Relations, self).__init__()
2156- for relname in sorted(hookenv.relation_types()):
2157- self[relname] = OrderedDict()
2158- relids = hookenv.relation_ids(relname)
2159- relids.sort(key=lambda x: int(x.split(':', 1)[-1]))
2160- for relid in relids:
2161- self[relname][relid] = Relation(relid)
2162-
2163- @property
2164- def peer(self):
2165- peer_relid = hookenv.peer_relation_id()
2166- for rels in self.values():
2167- if peer_relid in rels:
2168- return rels[peer_relid]
2169-
2170-
2171-class Relation(OrderedDict):
2172- '''Mapping of unit -> remote RelationInfo for a relation.
2173-
2174- This is an OrderedDict mapping, ordered numerically by
2175- by unit number.
2176-
2177- Also provides access to the local RelationInfo, and peer RelationInfo
2178- instances by the 'local' and 'peers' attributes.
2179-
2180- >>> r = Relation('sprog:12')
2181- >>> r.keys()
2182- ['client/9', 'client/10'] # Ordered numerically
2183- >>> r['client/10']['widget'] # A remote RelationInfo setting
2184- 'remote widget'
2185- >>> r.local['widget'] # The local RelationInfo setting
2186- 'local widget'
2187- '''
2188- relid = None # The relation id.
2189- relname = None # The relation name (also known as relation type).
2190- service = None # The remote service name, if known.
2191- local = None # The local end's RelationInfo.
2192- peers = None # Map of peer -> RelationInfo. None if no peer relation.
2193-
2194- def __init__(self, relid):
2195- remote_units = hookenv.related_units(relid)
2196- remote_units.sort(key=lambda u: int(u.split('/', 1)[-1]))
2197- super(Relation, self).__init__((unit, RelationInfo(relid, unit))
2198- for unit in remote_units)
2199-
2200- self.relname = relid.split(':', 1)[0]
2201- self.relid = relid
2202- self.local = RelationInfo(relid, hookenv.local_unit())
2203-
2204- for relinfo in self.values():
2205- self.service = relinfo.service
2206- break
2207-
2208- # If we have peers, and they have joined both the provided peer
2209- # relation and this relation, we can peek at their data too.
2210- # This is useful for creating consensus without leadership.
2211- peer_relid = hookenv.peer_relation_id()
2212- if peer_relid and peer_relid != relid:
2213- peers = hookenv.related_units(peer_relid)
2214- if peers:
2215- peers.sort(key=lambda u: int(u.split('/', 1)[-1]))
2216- self.peers = OrderedDict((peer, RelationInfo(relid, peer))
2217- for peer in peers)
2218- else:
2219- self.peers = OrderedDict()
2220- else:
2221- self.peers = None
2222-
2223- def __str__(self):
2224- return '{} ({})'.format(self.relid, self.service)
2225-
2226-
2227-class RelationInfo(UserDict):
2228- '''The bag of data at an end of a relation.
2229-
2230- Every unit participating in a relation has a single bag of
2231- data associated with that relation. This is that bag.
2232-
2233- The bag of data for the local unit may be updated. Remote data
2234- is immutable and will remain static for the duration of the hook.
2235-
2236- Changes made to the local units relation data only become visible
2237- to other units after the hook completes successfully. If the hook
2238- does not complete successfully, the changes are rolled back.
2239-
2240- Unlike standard Python mappings, setting an item to None is the
2241- same as deleting it.
2242-
2243- >>> relinfo = RelationInfo('db:12') # Default is the local unit.
2244- >>> relinfo['user'] = 'fred'
2245- >>> relinfo['user']
2246- 'fred'
2247- >>> relinfo['user'] = None
2248- >>> 'fred' in relinfo
2249- False
2250-
2251- This class wraps hookenv.relation_get and hookenv.relation_set.
2252- All caching is left up to these two methods to avoid synchronization
2253- issues. Data is only loaded on demand.
2254- '''
2255- relid = None # The relation id.
2256- relname = None # The relation name (also know as the relation type).
2257- unit = None # The unit id.
2258- number = None # The unit number (integer).
2259- service = None # The service name.
2260-
2261- def __init__(self, relid, unit):
2262- self.relname = relid.split(':', 1)[0]
2263- self.relid = relid
2264- self.unit = unit
2265- self.service, num = self.unit.split('/', 1)
2266- self.number = int(num)
2267-
2268- def __str__(self):
2269- return '{} ({})'.format(self.relid, self.unit)
2270-
2271- @property
2272- def data(self):
2273- return hookenv.relation_get(rid=self.relid, unit=self.unit)
2274-
2275- def __setitem__(self, key, value):
2276- if self.unit != hookenv.local_unit():
2277- raise TypeError('Attempting to set {} on remote unit {}'
2278- ''.format(key, self.unit))
2279- if value is not None and not isinstance(value, six.string_types):
2280- # We don't do implicit casting. This would cause simple
2281- # types like integers to be read back as strings in subsequent
2282- # hooks, and mutable types would require a lot of wrapping
2283- # to ensure relation-set gets called when they are mutated.
2284- raise ValueError('Only string values allowed')
2285- hookenv.relation_set(self.relid, {key: value})
2286-
2287- def __delitem__(self, key):
2288- # Deleting a key and setting it to null is the same thing in
2289- # Juju relations.
2290- self[key] = None
2291-
2292-
2293-class Leader(UserDict):
2294- def __init__(self):
2295- pass # Don't call superclass initializer, as it will nuke self.data
2296-
2297- @property
2298- def data(self):
2299- return hookenv.leader_get()
2300-
2301- def __setitem__(self, key, value):
2302- if not hookenv.is_leader():
2303- raise TypeError('Not the leader. Cannot change leader settings.')
2304- if value is not None and not isinstance(value, six.string_types):
2305- # We don't do implicit casting. This would cause simple
2306- # types like integers to be read back as strings in subsequent
2307- # hooks, and mutable types would require a lot of wrapping
2308- # to ensure leader-set gets called when they are mutated.
2309- raise ValueError('Only string values allowed')
2310- hookenv.leader_set({key: value})
2311-
2312- def __delitem__(self, key):
2313- # Deleting a key and setting it to null is the same thing in
2314- # Juju leadership settings.
2315- self[key] = None
2316
2317=== removed directory 'hooks/charmhelpers/contrib'
2318=== removed file 'hooks/charmhelpers/contrib/__init__.py'
2319--- hooks/charmhelpers/contrib/__init__.py 2015-08-06 12:08:02 +0000
2320+++ hooks/charmhelpers/contrib/__init__.py 1970-01-01 00:00:00 +0000
2321@@ -1,15 +0,0 @@
2322-# Copyright 2014-2015 Canonical Limited.
2323-#
2324-# This file is part of charm-helpers.
2325-#
2326-# charm-helpers is free software: you can redistribute it and/or modify
2327-# it under the terms of the GNU Lesser General Public License version 3 as
2328-# published by the Free Software Foundation.
2329-#
2330-# charm-helpers is distributed in the hope that it will be useful,
2331-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2332-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2333-# GNU Lesser General Public License for more details.
2334-#
2335-# You should have received a copy of the GNU Lesser General Public License
2336-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2337
2338=== removed directory 'hooks/charmhelpers/contrib/charmsupport'
2339=== removed file 'hooks/charmhelpers/contrib/charmsupport/__init__.py'
2340--- hooks/charmhelpers/contrib/charmsupport/__init__.py 2015-08-06 12:08:02 +0000
2341+++ hooks/charmhelpers/contrib/charmsupport/__init__.py 1970-01-01 00:00:00 +0000
2342@@ -1,15 +0,0 @@
2343-# Copyright 2014-2015 Canonical Limited.
2344-#
2345-# This file is part of charm-helpers.
2346-#
2347-# charm-helpers is free software: you can redistribute it and/or modify
2348-# it under the terms of the GNU Lesser General Public License version 3 as
2349-# published by the Free Software Foundation.
2350-#
2351-# charm-helpers is distributed in the hope that it will be useful,
2352-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2353-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2354-# GNU Lesser General Public License for more details.
2355-#
2356-# You should have received a copy of the GNU Lesser General Public License
2357-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2358
2359=== removed file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py'
2360--- hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-08-06 12:08:02 +0000
2361+++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000
2362@@ -1,360 +0,0 @@
2363-# Copyright 2014-2015 Canonical Limited.
2364-#
2365-# This file is part of charm-helpers.
2366-#
2367-# charm-helpers is free software: you can redistribute it and/or modify
2368-# it under the terms of the GNU Lesser General Public License version 3 as
2369-# published by the Free Software Foundation.
2370-#
2371-# charm-helpers is distributed in the hope that it will be useful,
2372-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2373-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2374-# GNU Lesser General Public License for more details.
2375-#
2376-# You should have received a copy of the GNU Lesser General Public License
2377-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2378-
2379-"""Compatibility with the nrpe-external-master charm"""
2380-# Copyright 2012 Canonical Ltd.
2381-#
2382-# Authors:
2383-# Matthew Wedgwood <matthew.wedgwood@canonical.com>
2384-
2385-import subprocess
2386-import pwd
2387-import grp
2388-import os
2389-import glob
2390-import shutil
2391-import re
2392-import shlex
2393-import yaml
2394-
2395-from charmhelpers.core.hookenv import (
2396- config,
2397- local_unit,
2398- log,
2399- relation_ids,
2400- relation_set,
2401- relations_of_type,
2402-)
2403-
2404-from charmhelpers.core.host import service
2405-
2406-# This module adds compatibility with the nrpe-external-master and plain nrpe
2407-# subordinate charms. To use it in your charm:
2408-#
2409-# 1. Update metadata.yaml
2410-#
2411-# provides:
2412-# (...)
2413-# nrpe-external-master:
2414-# interface: nrpe-external-master
2415-# scope: container
2416-#
2417-# and/or
2418-#
2419-# provides:
2420-# (...)
2421-# local-monitors:
2422-# interface: local-monitors
2423-# scope: container
2424-
2425-#
2426-# 2. Add the following to config.yaml
2427-#
2428-# nagios_context:
2429-# default: "juju"
2430-# type: string
2431-# description: |
2432-# Used by the nrpe subordinate charms.
2433-# A string that will be prepended to instance name to set the host name
2434-# in nagios. So for instance the hostname would be something like:
2435-# juju-myservice-0
2436-# If you're running multiple environments with the same services in them
2437-# this allows you to differentiate between them.
2438-# nagios_servicegroups:
2439-# default: ""
2440-# type: string
2441-# description: |
2442-# A comma-separated list of nagios servicegroups.
2443-# If left empty, the nagios_context will be used as the servicegroup
2444-#
2445-# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
2446-#
2447-# 4. Update your hooks.py with something like this:
2448-#
2449-# from charmsupport.nrpe import NRPE
2450-# (...)
2451-# def update_nrpe_config():
2452-# nrpe_compat = NRPE()
2453-# nrpe_compat.add_check(
2454-# shortname = "myservice",
2455-# description = "Check MyService",
2456-# check_cmd = "check_http -w 2 -c 10 http://localhost"
2457-# )
2458-# nrpe_compat.add_check(
2459-# "myservice_other",
2460-# "Check for widget failures",
2461-# check_cmd = "/srv/myapp/scripts/widget_check"
2462-# )
2463-# nrpe_compat.write()
2464-#
2465-# def config_changed():
2466-# (...)
2467-# update_nrpe_config()
2468-#
2469-# def nrpe_external_master_relation_changed():
2470-# update_nrpe_config()
2471-#
2472-# def local_monitors_relation_changed():
2473-# update_nrpe_config()
2474-#
2475-# 5. ln -s hooks.py nrpe-external-master-relation-changed
2476-# ln -s hooks.py local-monitors-relation-changed
2477-
2478-
2479-class CheckException(Exception):
2480- pass
2481-
2482-
2483-class Check(object):
2484- shortname_re = '[A-Za-z0-9-_]+$'
2485- service_template = ("""
2486-#---------------------------------------------------
2487-# This file is Juju managed
2488-#---------------------------------------------------
2489-define service {{
2490- use active-service
2491- host_name {nagios_hostname}
2492- service_description {nagios_hostname}[{shortname}] """
2493- """{description}
2494- check_command check_nrpe!{command}
2495- servicegroups {nagios_servicegroup}
2496-}}
2497-""")
2498-
2499- def __init__(self, shortname, description, check_cmd):
2500- super(Check, self).__init__()
2501- # XXX: could be better to calculate this from the service name
2502- if not re.match(self.shortname_re, shortname):
2503- raise CheckException("shortname must match {}".format(
2504- Check.shortname_re))
2505- self.shortname = shortname
2506- self.command = "check_{}".format(shortname)
2507- # Note: a set of invalid characters is defined by the
2508- # Nagios server config
2509- # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()=
2510- self.description = description
2511- self.check_cmd = self._locate_cmd(check_cmd)
2512-
2513- def _locate_cmd(self, check_cmd):
2514- search_path = (
2515- '/usr/lib/nagios/plugins',
2516- '/usr/local/lib/nagios/plugins',
2517- )
2518- parts = shlex.split(check_cmd)
2519- for path in search_path:
2520- if os.path.exists(os.path.join(path, parts[0])):
2521- command = os.path.join(path, parts[0])
2522- if len(parts) > 1:
2523- command += " " + " ".join(parts[1:])
2524- return command
2525- log('Check command not found: {}'.format(parts[0]))
2526- return ''
2527-
2528- def write(self, nagios_context, hostname, nagios_servicegroups):
2529- nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format(
2530- self.command)
2531- with open(nrpe_check_file, 'w') as nrpe_check_config:
2532- nrpe_check_config.write("# check {}\n".format(self.shortname))
2533- nrpe_check_config.write("command[{}]={}\n".format(
2534- self.command, self.check_cmd))
2535-
2536- if not os.path.exists(NRPE.nagios_exportdir):
2537- log('Not writing service config as {} is not accessible'.format(
2538- NRPE.nagios_exportdir))
2539- else:
2540- self.write_service_config(nagios_context, hostname,
2541- nagios_servicegroups)
2542-
2543- def write_service_config(self, nagios_context, hostname,
2544- nagios_servicegroups):
2545- for f in os.listdir(NRPE.nagios_exportdir):
2546- if re.search('.*{}.cfg'.format(self.command), f):
2547- os.remove(os.path.join(NRPE.nagios_exportdir, f))
2548-
2549- templ_vars = {
2550- 'nagios_hostname': hostname,
2551- 'nagios_servicegroup': nagios_servicegroups,
2552- 'description': self.description,
2553- 'shortname': self.shortname,
2554- 'command': self.command,
2555- }
2556- nrpe_service_text = Check.service_template.format(**templ_vars)
2557- nrpe_service_file = '{}/service__{}_{}.cfg'.format(
2558- NRPE.nagios_exportdir, hostname, self.command)
2559- with open(nrpe_service_file, 'w') as nrpe_service_config:
2560- nrpe_service_config.write(str(nrpe_service_text))
2561-
2562- def run(self):
2563- subprocess.call(self.check_cmd)
2564-
2565-
2566-class NRPE(object):
2567- nagios_logdir = '/var/log/nagios'
2568- nagios_exportdir = '/var/lib/nagios/export'
2569- nrpe_confdir = '/etc/nagios/nrpe.d'
2570-
2571- def __init__(self, hostname=None):
2572- super(NRPE, self).__init__()
2573- self.config = config()
2574- self.nagios_context = self.config['nagios_context']
2575- if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']:
2576- self.nagios_servicegroups = self.config['nagios_servicegroups']
2577- else:
2578- self.nagios_servicegroups = self.nagios_context
2579- self.unit_name = local_unit().replace('/', '-')
2580- if hostname:
2581- self.hostname = hostname
2582- else:
2583- self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
2584- self.checks = []
2585-
2586- def add_check(self, *args, **kwargs):
2587- self.checks.append(Check(*args, **kwargs))
2588-
2589- def write(self):
2590- try:
2591- nagios_uid = pwd.getpwnam('nagios').pw_uid
2592- nagios_gid = grp.getgrnam('nagios').gr_gid
2593- except:
2594- log("Nagios user not set up, nrpe checks not updated")
2595- return
2596-
2597- if not os.path.exists(NRPE.nagios_logdir):
2598- os.mkdir(NRPE.nagios_logdir)
2599- os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
2600-
2601- nrpe_monitors = {}
2602- monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}}
2603- for nrpecheck in self.checks:
2604- nrpecheck.write(self.nagios_context, self.hostname,
2605- self.nagios_servicegroups)
2606- nrpe_monitors[nrpecheck.shortname] = {
2607- "command": nrpecheck.command,
2608- }
2609-
2610- service('restart', 'nagios-nrpe-server')
2611-
2612- monitor_ids = relation_ids("local-monitors") + \
2613- relation_ids("nrpe-external-master")
2614- for rid in monitor_ids:
2615- relation_set(relation_id=rid, monitors=yaml.dump(monitors))
2616-
2617-
2618-def get_nagios_hostcontext(relation_name='nrpe-external-master'):
2619- """
2620- Query relation with nrpe subordinate, return the nagios_host_context
2621-
2622- :param str relation_name: Name of relation nrpe sub joined to
2623- """
2624- for rel in relations_of_type(relation_name):
2625- if 'nagios_hostname' in rel:
2626- return rel['nagios_host_context']
2627-
2628-
2629-def get_nagios_hostname(relation_name='nrpe-external-master'):
2630- """
2631- Query relation with nrpe subordinate, return the nagios_hostname
2632-
2633- :param str relation_name: Name of relation nrpe sub joined to
2634- """
2635- for rel in relations_of_type(relation_name):
2636- if 'nagios_hostname' in rel:
2637- return rel['nagios_hostname']
2638-
2639-
2640-def get_nagios_unit_name(relation_name='nrpe-external-master'):
2641- """
2642- Return the nagios unit name prepended with host_context if needed
2643-
2644- :param str relation_name: Name of relation nrpe sub joined to
2645- """
2646- host_context = get_nagios_hostcontext(relation_name)
2647- if host_context:
2648- unit = "%s:%s" % (host_context, local_unit())
2649- else:
2650- unit = local_unit()
2651- return unit
2652-
2653-
2654-def add_init_service_checks(nrpe, services, unit_name):
2655- """
2656- Add checks for each service in list
2657-
2658- :param NRPE nrpe: NRPE object to add check to
2659- :param list services: List of services to check
2660- :param str unit_name: Unit name to use in check description
2661- """
2662- for svc in services:
2663- upstart_init = '/etc/init/%s.conf' % svc
2664- sysv_init = '/etc/init.d/%s' % svc
2665- if os.path.exists(upstart_init):
2666- nrpe.add_check(
2667- shortname=svc,
2668- description='process check {%s}' % unit_name,
2669- check_cmd='check_upstart_job %s' % svc
2670- )
2671- elif os.path.exists(sysv_init):
2672- cronpath = '/etc/cron.d/nagios-service-check-%s' % svc
2673- cron_file = ('*/5 * * * * root '
2674- '/usr/local/lib/nagios/plugins/check_exit_status.pl '
2675- '-s /etc/init.d/%s status > '
2676- '/var/lib/nagios/service-check-%s.txt\n' % (svc,
2677- svc)
2678- )
2679- f = open(cronpath, 'w')
2680- f.write(cron_file)
2681- f.close()
2682- nrpe.add_check(
2683- shortname=svc,
2684- description='process check {%s}' % unit_name,
2685- check_cmd='check_status_file.py -f '
2686- '/var/lib/nagios/service-check-%s.txt' % svc,
2687- )
2688-
2689-
2690-def copy_nrpe_checks():
2691- """
2692- Copy the nrpe checks into place
2693-
2694- """
2695- NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
2696- nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks',
2697- 'charmhelpers', 'contrib', 'openstack',
2698- 'files')
2699-
2700- if not os.path.exists(NAGIOS_PLUGINS):
2701- os.makedirs(NAGIOS_PLUGINS)
2702- for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")):
2703- if os.path.isfile(fname):
2704- shutil.copy2(fname,
2705- os.path.join(NAGIOS_PLUGINS, os.path.basename(fname)))
2706-
2707-
2708-def add_haproxy_checks(nrpe, unit_name):
2709- """
2710- Add checks for each service in list
2711-
2712- :param NRPE nrpe: NRPE object to add check to
2713- :param str unit_name: Unit name to use in check description
2714- """
2715- nrpe.add_check(
2716- shortname='haproxy_servers',
2717- description='Check HAProxy {%s}' % unit_name,
2718- check_cmd='check_haproxy.sh')
2719- nrpe.add_check(
2720- shortname='haproxy_queue',
2721- description='Check HAProxy queue depth {%s}' % unit_name,
2722- check_cmd='check_haproxy_queue_depth.sh')
2723
2724=== removed file 'hooks/charmhelpers/coordinator.py'
2725--- hooks/charmhelpers/coordinator.py 2015-06-25 11:39:19 +0000
2726+++ hooks/charmhelpers/coordinator.py 1970-01-01 00:00:00 +0000
2727@@ -1,607 +0,0 @@
2728-# Copyright 2014-2015 Canonical Limited.
2729-#
2730-# This file is part of charm-helpers.
2731-#
2732-# charm-helpers is free software: you can redistribute it and/or modify
2733-# it under the terms of the GNU Lesser General Public License version 3 as
2734-# published by the Free Software Foundation.
2735-#
2736-# charm-helpers is distributed in the hope that it will be useful,
2737-# but WITHOUT ANY WARRANTY; without even the implied warranty of
2738-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2739-# GNU Lesser General Public License for more details.
2740-#
2741-# You should have received a copy of the GNU Lesser General Public License
2742-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
2743-'''
2744-The coordinator module allows you to use Juju's leadership feature to
2745-coordinate operations between units of a service.
2746-
2747-Behavior is defined in subclasses of coordinator.BaseCoordinator.
2748-One implementation is provided (coordinator.Serial), which allows an
2749-operation to be run on a single unit at a time, on a first come, first
2750-served basis. You can trivially define more complex behavior by
2751-subclassing BaseCoordinator or Serial.
2752-
2753-:author: Stuart Bishop <stuart.bishop@canonical.com>
2754-
2755-
2756-Services Framework Usage
2757-========================
2758-
2759-Ensure a peer relation is defined in metadata.yaml. Instantiate a
2760-BaseCoordinator subclass before invoking ServiceManager.manage().
2761-Ensure that ServiceManager.manage() is wired up to the leader-elected,
2762-leader-settings-changed, peer relation-changed and peer
2763-relation-departed hooks in addition to any other hooks you need, or your
2764-service will deadlock.
2765-
2766-Ensure calls to acquire() are guarded, so that locks are only requested
2767-when they are really needed (and thus hooks only triggered when necessary).
2768-Failing to do this and calling acquire() unconditionally will put your unit
2769-into a hook loop. Calls to granted() do not need to be guarded.
2770-
2771-For example::
2772-
2773- from charmhelpers.core import hookenv, services
2774- from charmhelpers import coordinator
2775-
2776- def maybe_restart(servicename):
2777- serial = coordinator.Serial()
2778- if needs_restart():
2779- serial.acquire('restart')
2780- if serial.granted('restart'):
2781- hookenv.service_restart(servicename)
2782-
2783- services = [dict(service='servicename',
2784- data_ready=[maybe_restart])]
2785-
2786- if __name__ == '__main__':
2787- _ = coordinator.Serial() # Must instantiate before manager.manage()
2788- manager = services.ServiceManager(services)
2789- manager.manage()
2790-
2791-
2792-You can implement a similar pattern using a decorator. If the lock has
2793-not been granted, an attempt to acquire() it will be made if the guard
2794-function returns True. If the lock has been granted, the decorated function
2795-is run as normal::
2796-
2797- from charmhelpers.core import hookenv, services
2798- from charmhelpers import coordinator
2799-
2800- serial = coordinator.Serial() # Global, instatiated on module import.
2801-
2802- def needs_restart():
2803- [ ... Introspect state. Return True if restart is needed ... ]
2804-
2805- @serial.require('restart', needs_restart)
2806- def maybe_restart(servicename):
2807- hookenv.service_restart(servicename)
2808-
2809- services = [dict(service='servicename',
2810- data_ready=[maybe_restart])]
2811-
2812- if __name__ == '__main__':
2813- manager = services.ServiceManager(services)
2814- manager.manage()
2815-
2816-
2817-Traditional Usage
2818-=================
2819-
2820-Ensure a peer relationis defined in metadata.yaml.
2821-
2822-If you are using charmhelpers.core.hookenv.Hooks, ensure that a
2823-BaseCoordinator subclass is instantiated before calling Hooks.execute.
2824-
2825-If you are not using charmhelpers.core.hookenv.Hooks, ensure
2826-that a BaseCoordinator subclass is instantiated and its handle()
2827-method called at the start of all your hooks.
2828-
2829-For example::
2830-
2831- import sys
2832- from charmhelpers.core import hookenv
2833- from charmhelpers import coordinator
2834-
2835- hooks = hookenv.Hooks()
2836-
2837- def maybe_restart():
2838- serial = coordinator.Serial()
2839- if serial.granted('restart'):
2840- hookenv.service_restart('myservice')
2841-
2842- @hooks.hook
2843- def config_changed():
2844- update_config()
2845- serial = coordinator.Serial()
2846- if needs_restart():
2847- serial.acquire('restart'):
2848- maybe_restart()
2849-
2850- # Cluster hooks must be wired up.
2851- @hooks.hook('cluster-relation-changed', 'cluster-relation-departed')
2852- def cluster_relation_changed():
2853- maybe_restart()
2854-
2855- # Leader hooks must be wired up.
2856- @hooks.hook('leader-elected', 'leader-settings-changed')
2857- def leader_settings_changed():
2858- maybe_restart()
2859-
2860- [ ... repeat for *all* other hooks you are using ... ]
2861-
2862- if __name__ == '__main__':
2863- _ = coordinator.Serial() # Must instantiate before execute()
2864- hooks.execute(sys.argv)
2865-
2866-
2867-You can also use the require decorator. If the lock has not been granted,
2868-an attempt to acquire() it will be made if the guard function returns True.
2869-If the lock has been granted, the decorated function is run as normal::
2870-
2871- from charmhelpers.core import hookenv
2872-
2873- hooks = hookenv.Hooks()
2874- serial = coordinator.Serial() # Must instantiate before execute()
2875-
2876- @require('restart', needs_restart)
2877- def maybe_restart():
2878- hookenv.service_restart('myservice')
2879-
2880- @hooks.hook('install', 'config-changed', 'upgrade-charm',
2881- # Peer and leader hooks must be wired up.
2882- 'cluster-relation-changed', 'cluster-relation-departed',
2883- 'leader-elected', 'leader-settings-changed')
2884- def default_hook():
2885- [...]
2886- maybe_restart()
2887-
2888- if __name__ == '__main__':
2889- hooks.execute()
2890-
2891-
2892-Details
2893-=======
2894-
2895-A simple API is provided similar to traditional locking APIs. A lock
2896-may be requested using the acquire() method, and the granted() method
2897-may be used do to check if a lock previously requested by acquire() has
2898-been granted. It doesn't matter how many times acquire() is called in a
2899-hook.
2900-
2901-Locks are released at the end of the hook they are acquired in. This may
2902-be the current hook if the unit is leader and the lock is free. It is
2903-more likely a future hook (probably leader-settings-changed, possibly
2904-the peer relation-changed or departed hook, potentially any hook).
2905-
2906-Whenever a charm needs to perform a coordinated action it will acquire()
2907-the lock and perform the action immediately if acquisition is
2908-successful. It will also need to perform the same action in every other
2909-hook if the lock has been granted.
2910-
2911-
2912-Grubby Details
2913---------------
2914-
2915-Why do you need to be able to perform the same action in every hook?
2916-If the unit is the leader, then it may be able to grant its own lock
2917-and perform the action immediately in the source hook. If the unit is
2918-the leader and cannot immediately grant the lock, then its only
2919-guaranteed chance of acquiring the lock is in the peer relation-joined,
2920-relation-changed or peer relation-departed hooks when another unit has
2921-released it (the only channel to communicate to the leader is the peer
2922-relation). If the unit is not the leader, then it is unlikely the lock
2923-is granted in the source hook (a previous hook must have also made the
2924-request for this to happen). A non-leader is notified about the lock via
2925-leader settings. These changes may be visible in any hook, even before
2926-the leader-settings-changed hook has been invoked. Or the requesting
2927-unit may be promoted to leader after making a request, in which case the
2928-lock may be granted in leader-elected or in a future peer
2929-relation-changed or relation-departed hook.
2930-
2931-This could be simpler if leader-settings-changed was invoked on the
2932-leader. We could then never grant locks except in
2933-leader-settings-changed hooks giving one place for the operation to be
2934-performed. Unfortunately this is not the case with Juju 1.23 leadership.
2935-
2936-But of course, this doesn't really matter to most people as most people
2937-seem to prefer the Services Framework or similar reset-the-world
2938-approaches, rather than the twisty maze of attempting to deduce what
2939-should be done based on what hook happens to be running (which always
2940-seems to evolve into reset-the-world anyway when the charm grows beyond
2941-the trivial).
2942-
2943-I chose not to implement a callback model, where a callback was passed
2944-to acquire to be executed when the lock is granted, because the callback
2945-may become invalid between making the request and the lock being granted
2946-due to an upgrade-charm being run in the interim. And it would create
2947-restrictions, such no lambdas, callback defined at the top level of a
2948-module, etc. Still, we could implement it on top of what is here, eg.
2949-by adding a defer decorator that stores a pickle of itself to disk and
2950-have BaseCoordinator unpickle and execute them when the locks are granted.
2951-'''
2952-from datetime import datetime
2953-from functools import wraps
2954-import json
2955-import os.path
2956-
2957-from six import with_metaclass
2958-
2959-from charmhelpers.core import hookenv
2960-
2961-
2962-# We make BaseCoordinator and subclasses singletons, so that if we
2963-# need to spill to local storage then only a single instance does so,
2964-# rather than having multiple instances stomp over each other.
2965-class Singleton(type):
2966- _instances = {}
2967-
2968- def __call__(cls, *args, **kwargs):
2969- if cls not in cls._instances:
2970- cls._instances[cls] = super(Singleton, cls).__call__(*args,
2971- **kwargs)
2972- return cls._instances[cls]
2973-
2974-
2975-class BaseCoordinator(with_metaclass(Singleton, object)):
2976- relid = None # Peer relation-id, set by __init__
2977- relname = None
2978-
2979- grants = None # self.grants[unit][lock] == timestamp
2980- requests = None # self.requests[unit][lock] == timestamp
2981-
2982- def __init__(self, relation_key='coordinator', peer_relation_name=None):
2983- '''Instatiate a Coordinator.
2984-
2985- Data is stored on the peer relation and in leadership storage
2986- under the provided relation_key.
2987-
2988- The peer relation is identified by peer_relation_name, and defaults
2989- to the first one found in metadata.yaml.
2990- '''
2991- # Most initialization is deferred, since invoking hook tools from
2992- # the constructor makes testing hard.
2993- self.key = relation_key
2994- self.relname = peer_relation_name
2995- hookenv.atstart(self.initialize)
2996-
2997- # Ensure that handle() is called, without placing that burden on
2998- # the charm author. They still need to do this manually if they
2999- # are not using a hook framework.
3000- hookenv.atstart(self.handle)
3001-
3002- def initialize(self):
3003- if self.requests is not None:
3004- return # Already initialized.
3005-
3006- assert hookenv.has_juju_version('1.23'), 'Needs Juju 1.23+'
3007-
3008- if self.relname is None:
3009- self.relname = _implicit_peer_relation_name()
3010-
3011- relids = hookenv.relation_ids(self.relname)
3012- if relids:
3013- self.relid = sorted(relids)[0]
3014-
3015- # Load our state, from leadership, the peer relationship, and maybe
3016- # local state as a fallback. Populates self.requests and self.grants.
3017- self._load_state()
3018- self._emit_state()
3019-
3020- # Save our state if the hook completes successfully.
3021- hookenv.atexit(self._save_state)
3022-
3023- # Schedule release of granted locks for the end of the hook.
3024- # This needs to be the last of our atexit callbacks to ensure
3025- # it will be run first when the hook is complete, because there
3026- # is no point mutating our state after it has been saved.
3027- hookenv.atexit(self._release_granted)
3028-
3029- def acquire(self, lock):
3030- '''Acquire the named lock, non-blocking.
3031-
3032- The lock may be granted immediately, or in a future hook.
3033-
3034- Returns True if the lock has been granted. The lock will be
3035- automatically released at the end of the hook in which it is
3036- granted.
3037-
3038- Do not mindlessly call this method, as it triggers a cascade of
3039- hooks. For example, if you call acquire() every time in your
3040- peer relation-changed hook you will end up with an infinite loop
3041- of hooks. It should almost always be guarded by some condition.
3042- '''
3043- unit = hookenv.local_unit()
3044- ts = self.requests[unit].get(lock)
3045- if not ts:
3046- # If there is no outstanding request on the peer relation,
3047- # create one.
3048- self.requests.setdefault(lock, {})
3049- self.requests[unit][lock] = _timestamp()
3050- self.msg('Requested {}'.format(lock))
3051-
3052- # If the leader has granted the lock, yay.
3053- if self.granted(lock):
3054- self.msg('Acquired {}'.format(lock))
3055- return True
3056-
3057- # If the unit making the request also happens to be the
3058- # leader, it must handle the request now. Even though the
3059- # request has been stored on the peer relation, the peer
3060- # relation-changed hook will not be triggered.
3061- if hookenv.is_leader():
3062- return self.grant(lock, unit)
3063-
3064- return False # Can't acquire lock, yet. Maybe next hook.
3065-
3066- def granted(self, lock):
3067- '''Return True if a previously requested lock has been granted'''
3068- unit = hookenv.local_unit()
3069- ts = self.requests[unit].get(lock)
3070- if ts and self.grants.get(unit, {}).get(lock) == ts:
3071- return True
3072- return False
3073-
3074- def requested(self, lock):
3075- '''Return True if we are in the queue for the lock'''
3076- return lock in self.requests[hookenv.local_unit()]
3077-
3078- def request_timestamp(self, lock):
3079- '''Return the timestamp of our outstanding request for lock, or None.
3080-
3081- Returns a datetime.datetime() UTC timestamp, with no tzinfo attribute.
3082- '''
3083- ts = self.requests[hookenv.local_unit()].get(lock, None)
3084- if ts is not None:
3085- return datetime.strptime(ts, _timestamp_format)
3086-
3087- def handle(self):
3088- if not hookenv.is_leader():
3089- return # Only the leader can grant requests.
3090-
3091- self.msg('Leader handling coordinator requests')
3092-
3093- # Clear our grants that have been released.
3094- for unit in self.grants.keys():
3095- for lock, grant_ts in list(self.grants[unit].items()):
3096- req_ts = self.requests.get(unit, {}).get(lock)
3097- if req_ts != grant_ts:
3098- # The request timestamp does not match the granted
3099- # timestamp. Several hooks on 'unit' may have run
3100- # before the leader got a chance to make a decision,
3101- # and 'unit' may have released its lock and attempted
3102- # to reacquire it. This will change the timestamp,
3103- # and we correctly revoke the old grant putting it
3104- # to the end of the queue.
3105- ts = datetime.strptime(self.grants[unit][lock],
3106- _timestamp_format)
3107- del self.grants[unit][lock]
3108- self.released(unit, lock, ts)
3109-
3110- # Grant locks
3111- for unit in self.requests.keys():
3112- for lock in self.requests[unit]:
3113- self.grant(lock, unit)
3114-
3115- def grant(self, lock, unit):
3116- '''Maybe grant the lock to a unit.
3117-
3118- The decision to grant the lock or not is made for $lock
3119- by a corresponding method grant_$lock, which you may define
3120- in a subclass. If no such method is defined, the default_grant
3121- method is used. See Serial.default_grant() for details.
3122- '''
3123- if not hookenv.is_leader():
3124- return False # Not the leader, so we cannot grant.
3125-
3126- # Set of units already granted the lock.
3127- granted = set()
3128- for u in self.grants:
3129- if lock in self.grants[u]:
3130- granted.add(u)
3131- if unit in granted:
3132- return True # Already granted.
3133-
3134- # Ordered list of units waiting for the lock.
3135- reqs = set()
3136- for u in self.requests:
3137- if u in granted:
3138- continue # In the granted set. Not wanted in the req list.
3139- for l, ts in self.requests[u].items():
3140- if l == lock:
3141- reqs.add((ts, u))
3142- queue = [t[1] for t in sorted(reqs)]
3143- if unit not in queue:
3144- return False # Unit has not requested the lock.
3145-
3146- # Locate custom logic, or fallback to the default.
3147- grant_func = getattr(self, 'grant_{}'.format(lock), self.default_grant)
3148-
3149- if grant_func(lock, unit, granted, queue):
3150- # Grant the lock.
3151- self.msg('Leader grants {} to {}'.format(lock, unit))
3152- self.grants.setdefault(unit, {})[lock] = self.requests[unit][lock]
3153- return True
3154-
3155- return False
3156-
3157- def released(self, unit, lock, timestamp):
3158- '''Called on the leader when it has released a lock.
3159-
3160- By default, does nothing but log messages. Override if you
3161- need to perform additional housekeeping when a lock is released,
3162- for example recording timestamps.
3163- '''
3164- interval = _utcnow() - timestamp
3165- self.msg('Leader released {} from {}, held {}'.format(lock, unit,
3166- interval))
3167-
3168- def require(self, lock, guard_func, *guard_args, **guard_kw):
3169- """Decorate a function to be run only when a lock is acquired.
3170-
3171- The lock is requested if the guard function returns True.
3172-
3173- The decorated function is called if the lock has been granted.
3174- """
3175- def decorator(f):
3176- @wraps(f)
3177- def wrapper(*args, **kw):
3178- if self.granted(lock):
3179- self.msg('Granted {}'.format(lock))
3180- return f(*args, **kw)
3181- if guard_func(*guard_args, **guard_kw) and self.acquire(lock):
3182- return f(*args, **kw)
3183- return None
3184- return wrapper
3185- return decorator
3186-
3187- def msg(self, msg):
3188- '''Emit a message. Override to customize log spam.'''
3189- hookenv.log('coordinator.{} {}'.format(self._name(), msg),
3190- level=hookenv.INFO)
3191-
3192- def _name(self):
3193- return self.__class__.__name__
3194-
3195- def _load_state(self):
3196- self.msg('Loading state'.format(self._name()))
3197-
3198- # All responses must be stored in the leadership settings.
3199- # The leader cannot use local state, as a different unit may
3200- # be leader next time. Which is fine, as the leadership
3201- # settings are always available.
3202- self.grants = json.loads(hookenv.leader_get(self.key) or '{}')
3203-
3204- local_unit = hookenv.local_unit()
3205-
3206- # All requests must be stored on the peer relation. This is
3207- # the only channel units have to communicate with the leader.
3208- # Even the leader needs to store its requests here, as a
3209- # different unit may be leader by the time the request can be
3210- # granted.
3211- if self.relid is None:
3212- # The peer relation is not available. Maybe we are early in
3213- # the units's lifecycle. Maybe this unit is standalone.
3214- # Fallback to using local state.
3215- self.msg('No peer relation. Loading local state')
3216- self.requests = {local_unit: self._load_local_state()}
3217- else:
3218- self.requests = self._load_peer_state()
3219- if local_unit not in self.requests:
3220- # The peer relation has just been joined. Update any state
3221- # loaded from our peers with our local state.
3222- self.msg('New peer relation. Merging local state')
3223- self.requests[local_unit] = self._load_local_state()
3224-
3225- def _emit_state(self):
3226- # Emit this units lock status.
3227- for lock in sorted(self.requests[hookenv.local_unit()].keys()):
3228- if self.granted(lock):
3229- self.msg('Granted {}'.format(lock))
3230- else:
3231- self.msg('Waiting on {}'.format(lock))
3232-
3233- def _save_state(self):
3234- self.msg('Publishing state'.format(self._name()))
3235- if hookenv.is_leader():
3236- # sort_keys to ensure stability.
3237- raw = json.dumps(self.grants, sort_keys=True)
3238- hookenv.leader_set({self.key: raw})
3239-
3240- local_unit = hookenv.local_unit()
3241-
3242- if self.relid is None:
3243- # No peer relation yet. Fallback to local state.
3244- self.msg('No peer relation. Saving local state')
3245- self._save_local_state(self.requests[local_unit])
3246- else:
3247- # sort_keys to ensure stability.
3248- raw = json.dumps(self.requests[local_unit], sort_keys=True)
3249- hookenv.relation_set(self.relid, relation_settings={self.key: raw})
3250-
3251- def _load_peer_state(self):
3252- requests = {}
3253- units = set(hookenv.related_units(self.relid))
3254- units.add(hookenv.local_unit())
3255- for unit in units:
3256- raw = hookenv.relation_get(self.key, unit, self.relid)
3257- if raw:
3258- requests[unit] = json.loads(raw)
3259- return requests
3260-
3261- def _local_state_filename(self):
3262- # Include the class name. We allow multiple BaseCoordinator
3263- # subclasses to be instantiated, and they are singletons, so
3264- # this avoids conflicts (unless someone creates and uses two
3265- # BaseCoordinator subclasses with the same class name, so don't
3266- # do that).
3267- return '.charmhelpers.coordinator.{}'.format(self._name())
3268-
3269- def _load_local_state(self):
3270- fn = self._local_state_filename()
3271- if os.path.exists(fn):
3272- with open(fn, 'r') as f:
3273- return json.load(f)
3274- return {}
3275-
3276- def _save_local_state(self, state):
3277- fn = self._local_state_filename()
3278- with open(fn, 'w') as f:
3279- json.dump(state, f)
3280-
3281- def _release_granted(self):
3282- # At the end of every hook, release all locks granted to
3283- # this unit. If a hook neglects to make use of what it
3284- # requested, it will just have to make the request again.
3285- # Implicit release is the only way this will work, as
3286- # if the unit is standalone there may be no future triggers
3287- # called to do a manual release.
3288- unit = hookenv.local_unit()
3289- for lock in list(self.requests[unit].keys()):
3290- if self.granted(lock):
3291- self.msg('Released local {} lock'.format(lock))
3292- del self.requests[unit][lock]
3293-
3294-
3295-class Serial(BaseCoordinator):
3296- def default_grant(self, lock, unit, granted, queue):
3297- '''Default logic to grant a lock to a unit. Unless overridden,
3298- only one unit may hold the lock and it will be granted to the
3299- earliest queued request.
3300-
3301- To define custom logic for $lock, create a subclass and
3302- define a grant_$lock method.
3303-
3304- `unit` is the unit name making the request.
3305-
3306- `granted` is the set of units already granted the lock. It will
3307- never include `unit`. It may be empty.
3308-
3309- `queue` is the list of units waiting for the lock, ordered by time
3310- of request. It will always include `unit`, but `unit` is not
3311- necessarily first.
3312-
3313- Returns True if the lock should be granted to `unit`.
3314- '''
3315- return unit == queue[0] and not granted
3316-
3317-
3318-def _implicit_peer_relation_name():
3319- md = hookenv.metadata()
3320- assert 'peers' in md, 'No peer relations in metadata.yaml'
3321- return sorted(md['peers'].keys())[0]
3322-
3323-
3324-# A human readable, sortable UTC timestamp format.
3325-_timestamp_format = '%Y-%m-%d %H:%M:%S.%fZ'
3326-
3327-
3328-def _utcnow(): # pragma: no cover
3329- # This wrapper exists as mocking datetime methods is problematic.
3330- return datetime.utcnow()
3331-
3332-
3333-def _timestamp():
3334- return _utcnow().strftime(_timestamp_format)
3335
3336=== removed directory 'hooks/charmhelpers/core'
3337=== removed file 'hooks/charmhelpers/core/__init__.py'
3338--- hooks/charmhelpers/core/__init__.py 2015-06-26 10:00:19 +0000
3339+++ hooks/charmhelpers/core/__init__.py 1970-01-01 00:00:00 +0000
3340@@ -1,15 +0,0 @@
3341-# Copyright 2014-2015 Canonical Limited.
3342-#
3343-# This file is part of charm-helpers.
3344-#
3345-# charm-helpers is free software: you can redistribute it and/or modify
3346-# it under the terms of the GNU Lesser General Public License version 3 as
3347-# published by the Free Software Foundation.
3348-#
3349-# charm-helpers is distributed in the hope that it will be useful,
3350-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3351-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3352-# GNU Lesser General Public License for more details.
3353-#
3354-# You should have received a copy of the GNU Lesser General Public License
3355-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
3356
3357=== removed file 'hooks/charmhelpers/core/decorators.py'
3358--- hooks/charmhelpers/core/decorators.py 2015-06-25 07:56:30 +0000
3359+++ hooks/charmhelpers/core/decorators.py 1970-01-01 00:00:00 +0000
3360@@ -1,57 +0,0 @@
3361-# Copyright 2014-2015 Canonical Limited.
3362-#
3363-# This file is part of charm-helpers.
3364-#
3365-# charm-helpers is free software: you can redistribute it and/or modify
3366-# it under the terms of the GNU Lesser General Public License version 3 as
3367-# published by the Free Software Foundation.
3368-#
3369-# charm-helpers is distributed in the hope that it will be useful,
3370-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3371-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3372-# GNU Lesser General Public License for more details.
3373-#
3374-# You should have received a copy of the GNU Lesser General Public License
3375-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
3376-
3377-#
3378-# Copyright 2014 Canonical Ltd.
3379-#
3380-# Authors:
3381-# Edward Hope-Morley <opentastic@gmail.com>
3382-#
3383-
3384-import time
3385-
3386-from charmhelpers.core.hookenv import (
3387- log,
3388- INFO,
3389-)
3390-
3391-
3392-def retry_on_exception(num_retries, base_delay=0, exc_type=Exception):
3393- """If the decorated function raises exception exc_type, allow num_retries
3394- retry attempts before raise the exception.
3395- """
3396- def _retry_on_exception_inner_1(f):
3397- def _retry_on_exception_inner_2(*args, **kwargs):
3398- retries = num_retries
3399- multiplier = 1
3400- while True:
3401- try:
3402- return f(*args, **kwargs)
3403- except exc_type:
3404- if not retries:
3405- raise
3406-
3407- delay = base_delay * multiplier
3408- multiplier += 1
3409- log("Retrying '%s' %d more times (delay=%s)" %
3410- (f.__name__, retries, delay), level=INFO)
3411- retries -= 1
3412- if delay:
3413- time.sleep(delay)
3414-
3415- return _retry_on_exception_inner_2
3416-
3417- return _retry_on_exception_inner_1
3418
3419=== removed file 'hooks/charmhelpers/core/files.py'
3420--- hooks/charmhelpers/core/files.py 2015-07-23 17:31:02 +0000
3421+++ hooks/charmhelpers/core/files.py 1970-01-01 00:00:00 +0000
3422@@ -1,45 +0,0 @@
3423-#!/usr/bin/env python
3424-# -*- coding: utf-8 -*-
3425-
3426-# Copyright 2014-2015 Canonical Limited.
3427-#
3428-# This file is part of charm-helpers.
3429-#
3430-# charm-helpers is free software: you can redistribute it and/or modify
3431-# it under the terms of the GNU Lesser General Public License version 3 as
3432-# published by the Free Software Foundation.
3433-#
3434-# charm-helpers is distributed in the hope that it will be useful,
3435-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3436-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3437-# GNU Lesser General Public License for more details.
3438-#
3439-# You should have received a copy of the GNU Lesser General Public License
3440-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
3441-
3442-__author__ = 'Jorge Niedbalski <niedbalski@ubuntu.com>'
3443-
3444-import os
3445-import subprocess
3446-
3447-
3448-def sed(filename, before, after, flags='g'):
3449- """
3450- Search and replaces the given pattern on filename.
3451-
3452- :param filename: relative or absolute file path.
3453- :param before: expression to be replaced (see 'man sed')
3454- :param after: expression to replace with (see 'man sed')
3455- :param flags: sed-compatible regex flags in example, to make
3456- the search and replace case insensitive, specify ``flags="i"``.
3457- The ``g`` flag is always specified regardless, so you do not
3458- need to remember to include it when overriding this parameter.
3459- :returns: If the sed command exit code was zero then return,
3460- otherwise raise CalledProcessError.
3461- """
3462- expression = r's/{0}/{1}/{2}'.format(before,
3463- after, flags)
3464-
3465- return subprocess.check_call(["sed", "-i", "-r", "-e",
3466- expression,
3467- os.path.expanduser(filename)])
3468
3469=== removed file 'hooks/charmhelpers/core/fstab.py'
3470--- hooks/charmhelpers/core/fstab.py 2015-06-26 10:00:19 +0000
3471+++ hooks/charmhelpers/core/fstab.py 1970-01-01 00:00:00 +0000
3472@@ -1,134 +0,0 @@
3473-#!/usr/bin/env python
3474-# -*- coding: utf-8 -*-
3475-
3476-# Copyright 2014-2015 Canonical Limited.
3477-#
3478-# This file is part of charm-helpers.
3479-#
3480-# charm-helpers is free software: you can redistribute it and/or modify
3481-# it under the terms of the GNU Lesser General Public License version 3 as
3482-# published by the Free Software Foundation.
3483-#
3484-# charm-helpers is distributed in the hope that it will be useful,
3485-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3486-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3487-# GNU Lesser General Public License for more details.
3488-#
3489-# You should have received a copy of the GNU Lesser General Public License
3490-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
3491-
3492-import io
3493-import os
3494-
3495-__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>'
3496-
3497-
3498-class Fstab(io.FileIO):
3499- """This class extends file in order to implement a file reader/writer
3500- for file `/etc/fstab`
3501- """
3502-
3503- class Entry(object):
3504- """Entry class represents a non-comment line on the `/etc/fstab` file
3505- """
3506- def __init__(self, device, mountpoint, filesystem,
3507- options, d=0, p=0):
3508- self.device = device
3509- self.mountpoint = mountpoint
3510- self.filesystem = filesystem
3511-
3512- if not options:
3513- options = "defaults"
3514-
3515- self.options = options
3516- self.d = int(d)
3517- self.p = int(p)
3518-
3519- def __eq__(self, o):
3520- return str(self) == str(o)
3521-
3522- def __str__(self):
3523- return "{} {} {} {} {} {}".format(self.device,
3524- self.mountpoint,
3525- self.filesystem,
3526- self.options,
3527- self.d,
3528- self.p)
3529-
3530- DEFAULT_PATH = os.path.join(os.path.sep, 'etc', 'fstab')
3531-
3532- def __init__(self, path=None):
3533- if path:
3534- self._path = path
3535- else:
3536- self._path = self.DEFAULT_PATH
3537- super(Fstab, self).__init__(self._path, 'rb+')
3538-
3539- def _hydrate_entry(self, line):
3540- # NOTE: use split with no arguments to split on any
3541- # whitespace including tabs
3542- return Fstab.Entry(*filter(
3543- lambda x: x not in ('', None),
3544- line.strip("\n").split()))
3545-
3546- @property
3547- def entries(self):
3548- self.seek(0)
3549- for line in self.readlines():
3550- line = line.decode('us-ascii')
3551- try:
3552- if line.strip() and not line.strip().startswith("#"):
3553- yield self._hydrate_entry(line)
3554- except ValueError:
3555- pass
3556-
3557- def get_entry_by_attr(self, attr, value):
3558- for entry in self.entries:
3559- e_attr = getattr(entry, attr)
3560- if e_attr == value:
3561- return entry
3562- return None
3563-
3564- def add_entry(self, entry):
3565- if self.get_entry_by_attr('device', entry.device):
3566- return False
3567-
3568- self.write((str(entry) + '\n').encode('us-ascii'))
3569- self.truncate()
3570- return entry
3571-
3572- def remove_entry(self, entry):
3573- self.seek(0)
3574-
3575- lines = [l.decode('us-ascii') for l in self.readlines()]
3576-
3577- found = False
3578- for index, line in enumerate(lines):
3579- if line.strip() and not line.strip().startswith("#"):
3580- if self._hydrate_entry(line) == entry:
3581- found = True
3582- break
3583-
3584- if not found:
3585- return False
3586-
3587- lines.remove(line)
3588-
3589- self.seek(0)
3590- self.write(''.join(lines).encode('us-ascii'))
3591- self.truncate()
3592- return True
3593-
3594- @classmethod
3595- def remove_by_mountpoint(cls, mountpoint, path=None):
3596- fstab = cls(path=path)
3597- entry = fstab.get_entry_by_attr('mountpoint', mountpoint)
3598- if entry:
3599- return fstab.remove_entry(entry)
3600- return False
3601-
3602- @classmethod
3603- def add(cls, device, mountpoint, filesystem, options=None, path=None):
3604- return cls(path=path).add_entry(Fstab.Entry(device,
3605- mountpoint, filesystem,
3606- options=options))
3607
3608=== removed file 'hooks/charmhelpers/core/hookenv.py'
3609--- hooks/charmhelpers/core/hookenv.py 2015-07-23 17:31:02 +0000
3610+++ hooks/charmhelpers/core/hookenv.py 1970-01-01 00:00:00 +0000
3611@@ -1,816 +0,0 @@
3612-# Copyright 2014-2015 Canonical Limited.
3613-#
3614-# This file is part of charm-helpers.
3615-#
3616-# charm-helpers is free software: you can redistribute it and/or modify
3617-# it under the terms of the GNU Lesser General Public License version 3 as
3618-# published by the Free Software Foundation.
3619-#
3620-# charm-helpers is distributed in the hope that it will be useful,
3621-# but WITHOUT ANY WARRANTY; without even the implied warranty of
3622-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3623-# GNU Lesser General Public License for more details.
3624-#
3625-# You should have received a copy of the GNU Lesser General Public License
3626-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
3627-
3628-"Interactions with the Juju environment"
3629-# Copyright 2013 Canonical Ltd.
3630-#
3631-# Authors:
3632-# Charm Helpers Developers <juju@lists.ubuntu.com>
3633-
3634-from __future__ import print_function
3635-import copy
3636-from distutils.version import LooseVersion
3637-from functools import wraps
3638-import glob
3639-import os
3640-import json
3641-import yaml
3642-import subprocess
3643-import sys
3644-import errno
3645-import tempfile
3646-from subprocess import CalledProcessError
3647-
3648-import six
3649-if not six.PY3:
3650- from UserDict import UserDict
3651-else:
3652- from collections import UserDict
3653-
3654-CRITICAL = "CRITICAL"
3655-ERROR = "ERROR"
3656-WARNING = "WARNING"
3657-INFO = "INFO"
3658-DEBUG = "DEBUG"
3659-MARKER = object()
3660-
3661-cache = {}
3662-
3663-
3664-def cached(func):
3665- """Cache return values for multiple executions of func + args
3666-
3667- For example::
3668-
3669- @cached
3670- def unit_get(attribute):
3671- pass
3672-
3673- unit_get('test')
3674-
3675- will cache the result of unit_get + 'test' for future calls.
3676- """
3677- @wraps(func)
3678- def wrapper(*args, **kwargs):
3679- global cache
3680- key = str((func, args, kwargs))
3681- try:
3682- return cache[key]
3683- except KeyError:
3684- pass # Drop out of the exception handler scope.
3685- res = func(*args, **kwargs)
3686- cache[key] = res
3687- return res
3688- return wrapper
3689-
3690-
3691-def flush(key):
3692- """Flushes any entries from function cache where the
3693- key is found in the function+args """
3694- flush_list = []
3695- for item in cache:
3696- if key in item:
3697- flush_list.append(item)
3698- for item in flush_list:
3699- del cache[item]
3700-
3701-
3702-def log(message, level=None):
3703- """Write a message to the juju log"""
3704- command = ['juju-log']
3705- if level:
3706- command += ['-l', level]
3707- if not isinstance(message, six.string_types):
3708- message = repr(message)
3709- command += [message]
3710- # Missing juju-log should not cause failures in unit tests
3711- # Send log output to stderr
3712- try:
3713- subprocess.call(command)
3714- except OSError as e:
3715- if e.errno == errno.ENOENT:
3716- if level:
3717- message = "{}: {}".format(level, message)
3718- message = "juju-log: {}".format(message)
3719- print(message, file=sys.stderr)
3720- else:
3721- raise
3722-
3723-
3724-class Serializable(UserDict):
3725- """Wrapper, an object that can be serialized to yaml or json"""
3726-
3727- def __init__(self, obj):
3728- # wrap the object
3729- UserDict.__init__(self)
3730- self.data = obj
3731-
3732- def __getattr__(self, attr):
3733- # See if this object has attribute.
3734- if attr in ("json", "yaml", "data"):
3735- return self.__dict__[attr]
3736- # Check for attribute in wrapped object.
3737- got = getattr(self.data, attr, MARKER)
3738- if got is not MARKER:
3739- return got
3740- # Proxy to the wrapped object via dict interface.
3741- try:
3742- return self.data[attr]
3743- except KeyError:
3744- raise AttributeError(attr)
3745-
3746- def __getstate__(self):
3747- # Pickle as a standard dictionary.
3748- return self.data
3749-
3750- def __setstate__(self, state):
3751- # Unpickle into our wrapper.
3752- self.data = state
3753-
3754- def json(self):
3755- """Serialize the object to json"""
3756- return json.dumps(self.data)
3757-
3758- def yaml(self):
3759- """Serialize the object to yaml"""
3760- return yaml.dump(self.data)
3761-
3762-
3763-def execution_environment():
3764- """A convenient bundling of the current execution context"""
3765- context = {}
3766- context['conf'] = config()
3767- if relation_id():
3768- context['reltype'] = relation_type()
3769- context['relid'] = relation_id()
3770- context['rel'] = relation_get()
3771- context['unit'] = local_unit()
3772- context['rels'] = relations()
3773- context['env'] = os.environ
3774- return context
3775-
3776-
3777-def in_relation_hook():
3778- """Determine whether we're running in a relation hook"""
3779- return 'JUJU_RELATION' in os.environ
3780-
3781-
3782-def relation_type():
3783- """The scope for the current relation hook"""
3784- return os.environ.get('JUJU_RELATION', None)
3785-
3786-
3787-def relation_id():
3788- """The relation ID for the current relation hook"""
3789- return os.environ.get('JUJU_RELATION_ID', None)
3790-
3791-
3792-def local_unit():
3793- """Local unit ID"""
3794- return os.environ['JUJU_UNIT_NAME']
3795-
3796-
3797-def remote_unit():
3798- """The remote unit for the current relation hook"""
3799- return os.environ.get('JUJU_REMOTE_UNIT', None)
3800-
3801-
3802-def service_name():
3803- """The name service group this unit belongs to"""
3804- return local_unit().split('/')[0]
3805-
3806-
3807-def hook_name():
3808- """The name of the currently executing hook"""
3809- return os.path.basename(sys.argv[0])
3810-
3811-
3812-class Config(dict):
3813- """A dictionary representation of the charm's config.yaml, with some
3814- extra features:
3815-
3816- - See which values in the dictionary have changed since the previous hook.
3817- - For values that have changed, see what the previous value was.
3818- - Store arbitrary data for use in a later hook.
3819-
3820- NOTE: Do not instantiate this object directly - instead call
3821- ``hookenv.config()``, which will return an instance of :class:`Config`.
3822-
3823- Example usage::
3824-
3825- >>> # inside a hook
3826- >>> from charmhelpers.core import hookenv
3827- >>> config = hookenv.config()
3828- >>> config['foo']
3829- 'bar'
3830- >>> # store a new key/value for later use
3831- >>> config['mykey'] = 'myval'
3832-
3833-
3834- >>> # user runs `juju set mycharm foo=baz`
3835- >>> # now we're inside subsequent config-changed hook
3836- >>> config = hookenv.config()
3837- >>> config['foo']
3838- 'baz'
3839- >>> # test to see if this val has changed since last hook
3840- >>> config.changed('foo')
3841- True
3842- >>> # what was the previous value?
3843- >>> config.previous('foo')
3844- 'bar'
3845- >>> # keys/values that we add are preserved across hooks
3846- >>> config['mykey']
3847- 'myval'
3848-
3849- """
3850- CONFIG_FILE_NAME = '.juju-persistent-config'
3851-
3852- def __init__(self, *args, **kw):
3853- super(Config, self).__init__(*args, **kw)
3854- self.implicit_save = True
3855- self._prev_dict = None
3856- self.path = os.path.join(charm_dir(), Config.CONFIG_FILE_NAME)
3857- if os.path.exists(self.path):
3858- self.load_previous()
3859- atexit(self._implicit_save)
3860-
3861- def load_previous(self, path=None):
3862- """Load previous copy of config from disk.
3863-
3864- In normal usage you don't need to call this method directly - it
3865- is called automatically at object initialization.
3866-
3867- :param path:
3868-
3869- File path from which to load the previous config. If `None`,
3870- config is loaded from the default location. If `path` is
3871- specified, subsequent `save()` calls will write to the same
3872- path.
3873-
3874- """
3875- self.path = path or self.path
3876- with open(self.path) as f:
3877- self._prev_dict = json.load(f)
3878- for k, v in copy.deepcopy(self._prev_dict).items():
3879- if k not in self:
3880- self[k] = v
3881-
3882- def changed(self, key):
3883- """Return True if the current value for this key is different from
3884- the previous value.
3885-
3886- """
3887- if self._prev_dict is None:
3888- return True
3889- return self.previous(key) != self.get(key)
3890-
3891- def previous(self, key):
3892- """Return previous value for this key, or None if there
3893- is no previous value.
3894-
3895- """
3896- if self._prev_dict:
3897- return self._prev_dict.get(key)
3898- return None
3899-
3900- def save(self):
3901- """Save this config to disk.
3902-
3903- If the charm is using the :mod:`Services Framework <services.base>`
3904- or :meth:'@hook <Hooks.hook>' decorator, this
3905- is called automatically at the end of successful hook execution.
3906- Otherwise, it should be called directly by user code.
3907-
3908- To disable automatic saves, set ``implicit_save=False`` on this
3909- instance.
3910-
3911- """
3912- with open(self.path, 'w') as f:
3913- json.dump(self, f)
3914-
3915- def _implicit_save(self):
3916- if self.implicit_save:
3917- self.save()
3918-
3919-
3920-@cached
3921-def config(scope=None):
3922- """Juju charm configuration"""
3923- config_cmd_line = ['config-get']
3924- if scope is not None:
3925- config_cmd_line.append(scope)
3926- config_cmd_line.append('--format=json')
3927- try:
3928- config_data = json.loads(
3929- subprocess.check_output(config_cmd_line).decode('UTF-8'))
3930- if scope is not None:
3931- return config_data
3932- return Config(config_data)
3933- except ValueError:
3934- return None
3935-
3936-
3937-@cached
3938-def relation_get(attribute=None, unit=None, rid=None):
3939- """Get relation information"""
3940- _args = ['relation-get', '--format=json']
3941- if rid:
3942- _args.append('-r')
3943- _args.append(rid)
3944- _args.append(attribute or '-')
3945- if unit:
3946- _args.append(unit)
3947- try:
3948- return json.loads(subprocess.check_output(_args).decode('UTF-8'))
3949- except ValueError:
3950- return None
3951- except CalledProcessError as e:
3952- if e.returncode == 2:
3953- return None
3954- raise
3955-
3956-
3957-def relation_set(relation_id=None, relation_settings=None, **kwargs):
3958- """Set relation information for the current unit"""
3959- relation_settings = relation_settings if relation_settings else {}
3960- relation_cmd_line = ['relation-set']
3961- accepts_file = "--file" in subprocess.check_output(
3962- relation_cmd_line + ["--help"], universal_newlines=True)
3963- if relation_id is not None:
3964- relation_cmd_line.extend(('-r', relation_id))
3965- settings = relation_settings.copy()
3966- settings.update(kwargs)
3967- for key, value in settings.items():
3968- # Force value to be a string: it always should, but some call
3969- # sites pass in things like dicts or numbers.
3970- if value is not None:
3971- settings[key] = "{}".format(value)
3972- if accepts_file:
3973- # --file was introduced in Juju 1.23.2. Use it by default if
3974- # available, since otherwise we'll break if the relation data is
3975- # too big. Ideally we should tell relation-set to read the data from
3976- # stdin, but that feature is broken in 1.23.2: Bug #1454678.
3977- with tempfile.NamedTemporaryFile(delete=False) as settings_file:
3978- settings_file.write(yaml.safe_dump(settings).encode("utf-8"))
3979- subprocess.check_call(
3980- relation_cmd_line + ["--file", settings_file.name])
3981- os.remove(settings_file.name)
3982- else:
3983- for key, value in settings.items():
3984- if value is None:
3985- relation_cmd_line.append('{}='.format(key))
3986- else:
3987- relation_cmd_line.append('{}={}'.format(key, value))
3988- subprocess.check_call(relation_cmd_line)
3989- # Flush cache of any relation-gets for local unit
3990- flush(local_unit())
3991-
3992-
3993-def relation_clear(r_id=None):
3994- ''' Clears any relation data already set on relation r_id '''
3995- settings = relation_get(rid=r_id,
3996- unit=local_unit())
3997- for setting in settings:
3998- if setting not in ['public-address', 'private-address']:
3999- settings[setting] = None
4000- relation_set(relation_id=r_id,
4001- **settings)
4002-
4003-
4004-@cached
4005-def relation_ids(reltype=None):
4006- """A list of relation_ids"""
4007- reltype = reltype or relation_type()
4008- relid_cmd_line = ['relation-ids', '--format=json']
4009- if reltype is not None:
4010- relid_cmd_line.append(reltype)
4011- return json.loads(
4012- subprocess.check_output(relid_cmd_line).decode('UTF-8')) or []
4013- return []
4014-
4015-
4016-@cached
4017-def related_units(relid=None):
4018- """A list of related units"""
4019- relid = relid or relation_id()
4020- units_cmd_line = ['relation-list', '--format=json']
4021- if relid is not None:
4022- units_cmd_line.extend(('-r', relid))
4023- return json.loads(
4024- subprocess.check_output(units_cmd_line).decode('UTF-8')) or []
4025-
4026-
4027-@cached
4028-def relation_for_unit(unit=None, rid=None):
4029- """Get the json represenation of a unit's relation"""
4030- unit = unit or remote_unit()
4031- relation = relation_get(unit=unit, rid=rid)
4032- for key in relation:
4033- if key.endswith('-list'):
4034- relation[key] = relation[key].split()
4035- relation['__unit__'] = unit
4036- return relation
4037-
4038-
4039-@cached
4040-def relations_for_id(relid=None):
4041- """Get relations of a specific relation ID"""
4042- relation_data = []
4043- relid = relid or relation_ids()
4044- for unit in related_units(relid):
4045- unit_data = relation_for_unit(unit, relid)
4046- unit_data['__relid__'] = relid
4047- relation_data.append(unit_data)
4048- return relation_data
4049-
4050-
4051-@cached
4052-def relations_of_type(reltype=None):
4053- """Get relations of a specific type"""
4054- relation_data = []
4055- reltype = reltype or relation_type()
4056- for relid in relation_ids(reltype):
4057- for relation in relations_for_id(relid):
4058- relation['__relid__'] = relid
4059- relation_data.append(relation)
4060- return relation_data
4061-
4062-
4063-@cached
4064-def metadata():
4065- """Get the current charm metadata.yaml contents as a python object"""
4066- with open(os.path.join(charm_dir(), 'metadata.yaml')) as md:
4067- return yaml.safe_load(md)
4068-
4069-
4070-@cached
4071-def relation_types():
4072- """Get a list of relation types supported by this charm"""
4073- rel_types = []
4074- md = metadata()
4075- for key in ('provides', 'requires', 'peers'):
4076- section = md.get(key)
4077- if section:
4078- rel_types.extend(section.keys())
4079- return rel_types
4080-
4081-
4082-@cached
4083-def peer_relation_id():
4084- '''Get a peer relation id if a peer relation has been joined, else None.'''
4085- md = metadata()
4086- section = md.get('peers')
4087- if section:
4088- for key in section:
4089- relids = relation_ids(key)
4090- if relids:
4091- return relids[0]
4092- return None
4093-
4094-
4095-@cached
4096-def charm_name():
4097- """Get the name of the current charm as is specified on metadata.yaml"""
4098- return metadata().get('name')
4099-
4100-
4101-@cached
4102-def relations():
4103- """Get a nested dictionary of relation data for all related units"""
4104- rels = {}
4105- for reltype in relation_types():
4106- relids = {}
4107- for relid in relation_ids(reltype):
4108- units = {local_unit(): relation_get(unit=local_unit(), rid=relid)}
4109- for unit in related_units(relid):
4110- reldata = relation_get(unit=unit, rid=relid)
4111- units[unit] = reldata
4112- relids[relid] = units
4113- rels[reltype] = relids
4114- return rels
4115-
4116-
4117-@cached
4118-def is_relation_made(relation, keys='private-address'):
4119- '''
4120- Determine whether a relation is established by checking for
4121- presence of key(s). If a list of keys is provided, they
4122- must all be present for the relation to be identified as made
4123- '''
4124- if isinstance(keys, str):
4125- keys = [keys]
4126- for r_id in relation_ids(relation):
4127- for unit in related_units(r_id):
4128- context = {}
4129- for k in keys:
4130- context[k] = relation_get(k, rid=r_id,
4131- unit=unit)
4132- if None not in context.values():
4133- return True
4134- return False
4135-
4136-
4137-def open_port(port, protocol="TCP"):
4138- """Open a service network port"""
4139- _args = ['open-port']
4140- _args.append('{}/{}'.format(port, protocol))
4141- subprocess.check_call(_args)
4142-
4143-
4144-def close_port(port, protocol="TCP"):
4145- """Close a service network port"""
4146- _args = ['close-port']
4147- _args.append('{}/{}'.format(port, protocol))
4148- subprocess.check_call(_args)
4149-
4150-
4151-@cached
4152-def unit_get(attribute):
4153- """Get the unit ID for the remote unit"""
4154- _args = ['unit-get', '--format=json', attribute]
4155- try:
4156- return json.loads(subprocess.check_output(_args).decode('UTF-8'))
4157- except ValueError:
4158- return None
4159-
4160-
4161-def unit_public_ip():
4162- """Get this unit's public IP address"""
4163- return unit_get('public-address')
4164-
4165-
4166-def unit_private_ip():
4167- """Get this unit's private IP address"""
4168- return unit_get('private-address')
4169-
4170-
4171-class UnregisteredHookError(Exception):
4172- """Raised when an undefined hook is called"""
4173- pass
4174-
4175-
4176-class Hooks(object):
4177- """A convenient handler for hook functions.
4178-
4179- Example::
4180-
4181- hooks = Hooks()
4182-
4183- # register a hook, taking its name from the function name
4184- @hooks.hook()
4185- def install():
4186- pass # your code here
4187-
4188- # register a hook, providing a custom hook name
4189- @hooks.hook("config-changed")
4190- def config_changed():
4191- pass # your code here
4192-
4193- if __name__ == "__main__":
4194- # execute a hook based on the name the program is called by
4195- hooks.execute(sys.argv)
4196- """
4197-
4198- def __init__(self, config_save=None):
4199- super(Hooks, self).__init__()
4200- self._hooks = {}
4201-
4202- # For unknown reasons, we allow the Hooks constructor to override
4203- # config().implicit_save.
4204- if config_save is not None:
4205- config().implicit_save = config_save
4206-
4207- def register(self, name, function):
4208- """Register a hook"""
4209- self._hooks[name] = function
4210-
4211- def execute(self, args):
4212- """Execute a registered hook based on args[0]"""
4213- _run_atstart()
4214- hook_name = os.path.basename(args[0])
4215- if hook_name in self._hooks:
4216- try:
4217- self._hooks[hook_name]()
4218- except SystemExit as x:
4219- if x.code is None or x.code == 0:
4220- _run_atexit()
4221- raise
4222- _run_atexit()
4223- else:
4224- raise UnregisteredHookError(hook_name)
4225-
4226- def hook(self, *hook_names):
4227- """Decorator, registering them as hooks"""
4228- def wrapper(decorated):
4229- for hook_name in hook_names:
4230- self.register(hook_name, decorated)
4231- else:
4232- self.register(decorated.__name__, decorated)
4233- if '_' in decorated.__name__:
4234- self.register(
4235- decorated.__name__.replace('_', '-'), decorated)
4236- return decorated
4237- return wrapper
4238-
4239-
4240-def charm_dir():
4241- """Return the root directory of the current charm"""
4242- return os.environ.get('CHARM_DIR')
4243-
4244-
4245-@cached
4246-def action_get(key=None):
4247- """Gets the value of an action parameter, or all key/value param pairs"""
4248- cmd = ['action-get']
4249- if key is not None:
4250- cmd.append(key)
4251- cmd.append('--format=json')
4252- action_data = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
4253- return action_data
4254-
4255-
4256-def action_set(values):
4257- """Sets the values to be returned after the action finishes"""
4258- cmd = ['action-set']
4259- for k, v in list(values.items()):
4260- cmd.append('{}={}'.format(k, v))
4261- subprocess.check_call(cmd)
4262-
4263-
4264-def action_fail(message):
4265- """Sets the action status to failed and sets the error message.
4266-
4267- The results set by action_set are preserved."""
4268- subprocess.check_call(['action-fail', message])
4269-
4270-
4271-def status_set(workload_state, message):
4272- """Set the workload state with a message
4273-
4274- Use status-set to set the workload state with a message which is visible
4275- to the user via juju status. If the status-set command is not found then
4276- assume this is juju < 1.23 and juju-log the message unstead.
4277-
4278- workload_state -- valid juju workload state.
4279- message -- status update message
4280- """
4281- valid_states = ['maintenance', 'blocked', 'waiting', 'active']
4282- if workload_state not in valid_states:
4283- raise ValueError(
4284- '{!r} is not a valid workload state'.format(workload_state)
4285- )
4286- cmd = ['status-set', workload_state, message]
4287- try:
4288- ret = subprocess.call(cmd)
4289- if ret == 0:
4290- return
4291- except OSError as e:
4292- if e.errno != errno.ENOENT:
4293- raise
4294- log_message = 'status-set failed: {} {}'.format(workload_state,
4295- message)
4296- log(log_message, level='INFO')
4297-
4298-
4299-def status_get():
4300- """Retrieve the previously set juju workload state
4301-
4302- If the status-set command is not found then assume this is juju < 1.23 and
4303- return 'unknown'
4304- """
4305- cmd = ['status-get']
4306- try:
4307- raw_status = subprocess.check_output(cmd, universal_newlines=True)
4308- status = raw_status.rstrip()
4309- return status
4310- except OSError as e:
4311- if e.errno == errno.ENOENT:
4312- return 'unknown'
4313- else:
4314- raise
4315-
4316-
4317-def translate_exc(from_exc, to_exc):
4318- def inner_translate_exc1(f):
4319- @wraps(f)
4320- def inner_translate_exc2(*args, **kwargs):
4321- try:
4322- return f(*args, **kwargs)
4323- except from_exc:
4324- raise to_exc
4325-
4326- return inner_translate_exc2
4327-
4328- return inner_translate_exc1
4329-
4330-
4331-@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
4332-def is_leader():
4333- """Does the current unit hold the juju leadership
4334-
4335- Uses juju to determine whether the current unit is the leader of its peers
4336- """
4337- cmd = ['is-leader', '--format=json']
4338- return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
4339-
4340-
4341-@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
4342-def leader_get(attribute=None):
4343- """Juju leader get value(s)"""
4344- cmd = ['leader-get', '--format=json'] + [attribute or '-']
4345- return json.loads(subprocess.check_output(cmd).decode('UTF-8'))
4346-
4347-
4348-@translate_exc(from_exc=OSError, to_exc=NotImplementedError)
4349-def leader_set(settings=None, **kwargs):
4350- """Juju leader set value(s)"""
4351- # Don't log secrets.
4352- # log("Juju leader-set '%s'" % (settings), level=DEBUG)
4353- cmd = ['leader-set']
4354- settings = settings or {}
4355- settings.update(kwargs)
4356- for k, v in settings.items():
4357- if v is None:
4358- cmd.append('{}='.format(k))
4359- else:
4360- cmd.append('{}={}'.format(k, v))
4361- subprocess.check_call(cmd)
4362-
4363-
4364-@cached
4365-def juju_version():
4366- """Full version string (eg. '1.23.3.1-trusty-amd64')"""
4367- # Per https://bugs.launchpad.net/juju-core/+bug/1455368/comments/1
4368- jujud = glob.glob('/var/lib/juju/tools/machine-*/jujud')[0]
4369- return subprocess.check_output([jujud, 'version'],
4370- universal_newlines=True).strip()
4371-
4372-
4373-@cached
4374-def has_juju_version(minimum_version):
4375- """Return True if the Juju version is at least the provided version"""
4376- return LooseVersion(juju_version()) >= LooseVersion(minimum_version)
4377-
4378-
4379-_atexit = []
4380-_atstart = []
4381-
4382-
4383-def atstart(callback, *args, **kwargs):
4384- '''Schedule a callback to run before the main hook.
4385-
4386- Callbacks are run in the order they were added.
4387-
4388- This is useful for modules and classes to perform initialization
4389- and inject behavior. In particular:
4390-
4391- - Run common code before all of your hooks, such as logging
4392- the hook name or interesting relation data.
4393- - Defer object or module initialization that requires a hook
4394- context until we know there actually is a hook context,
4395- making testing easier.
4396- - Rather than requiring charm authors to include boilerplate to
4397- invoke your helper's behavior, have it run automatically if
4398- your object is instantiated or module imported.
4399-
4400- This is not at all useful after your hook framework as been launched.
4401- '''
4402- global _atstart
4403- _atstart.append((callback, args, kwargs))
4404-
4405-
4406-def atexit(callback, *args, **kwargs):
4407- '''Schedule a callback to run on successful hook completion.
4408-
4409- Callbacks are run in the reverse order that they were added.'''
4410- _atexit.append((callback, args, kwargs))
4411-
4412-
4413-def _run_atstart():
4414- '''Hook frameworks must invoke this before running the main hook body.'''
4415- global _atstart
4416- for callback, args, kwargs in _atstart:
4417- callback(*args, **kwargs)
4418- del _atstart[:]
4419-
4420-
4421-def _run_atexit():
4422- '''Hook frameworks must invoke this after the main hook body has
4423- successfully completed. Do not invoke it if the hook fails.'''
4424- global _atexit
4425- for callback, args, kwargs in reversed(_atexit):
4426- callback(*args, **kwargs)
4427- del _atexit[:]
4428
4429=== removed file 'hooks/charmhelpers/core/host.py'
4430--- hooks/charmhelpers/core/host.py 2015-07-27 10:11:54 +0000
4431+++ hooks/charmhelpers/core/host.py 1970-01-01 00:00:00 +0000
4432@@ -1,510 +0,0 @@
4433-# Copyright 2014-2015 Canonical Limited.
4434-#
4435-# This file is part of charm-helpers.
4436-#
4437-# charm-helpers is free software: you can redistribute it and/or modify
4438-# it under the terms of the GNU Lesser General Public License version 3 as
4439-# published by the Free Software Foundation.
4440-#
4441-# charm-helpers is distributed in the hope that it will be useful,
4442-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4443-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4444-# GNU Lesser General Public License for more details.
4445-#
4446-# You should have received a copy of the GNU Lesser General Public License
4447-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
4448-
4449-"""Tools for working with the host system"""
4450-# Copyright 2012 Canonical Ltd.
4451-#
4452-# Authors:
4453-# Nick Moffitt <nick.moffitt@canonical.com>
4454-# Matthew Wedgwood <matthew.wedgwood@canonical.com>
4455-
4456-import os
4457-import re
4458-import pwd
4459-import glob
4460-import grp
4461-import random
4462-import string
4463-import subprocess
4464-import hashlib
4465-from contextlib import contextmanager
4466-from collections import OrderedDict
4467-
4468-import six
4469-
4470-from .hookenv import log
4471-from .fstab import Fstab
4472-
4473-
4474-def service_start(service_name):
4475- """Start a system service"""
4476- return service('start', service_name)
4477-
4478-
4479-def service_stop(service_name):
4480- """Stop a system service"""
4481- return service('stop', service_name)
4482-
4483-
4484-def service_restart(service_name):
4485- """Restart a system service"""
4486- return service('restart', service_name)
4487-
4488-
4489-def service_reload(service_name, restart_on_failure=False):
4490- """Reload a system service, optionally falling back to restart if
4491- reload fails"""
4492- service_result = service('reload', service_name)
4493- if not service_result and restart_on_failure:
4494- service_result = service('restart', service_name)
4495- return service_result
4496-
4497-
4498-def service_pause(service_name, init_dir=None):
4499- """Pause a system service.
4500-
4501- Stop it, and prevent it from starting again at boot."""
4502- if init_dir is None:
4503- init_dir = "/etc/init"
4504- stopped = service_stop(service_name)
4505- # XXX: Support systemd too
4506- override_path = os.path.join(
4507- init_dir, '{}.conf.override'.format(service_name))
4508- with open(override_path, 'w') as fh:
4509- fh.write("manual\n")
4510- return stopped
4511-
4512-
4513-def service_resume(service_name, init_dir=None):
4514- """Resume a system service.
4515-
4516- Reenable starting again at boot. Start the service"""
4517- # XXX: Support systemd too
4518- if init_dir is None:
4519- init_dir = "/etc/init"
4520- override_path = os.path.join(
4521- init_dir, '{}.conf.override'.format(service_name))
4522- if os.path.exists(override_path):
4523- os.unlink(override_path)
4524- started = service_start(service_name)
4525- return started
4526-
4527-
4528-def service(action, service_name):
4529- """Control a system service"""
4530- cmd = ['service', service_name, action]
4531- return subprocess.call(cmd) == 0
4532-
4533-
4534-def service_running(service):
4535- """Determine whether a system service is running"""
4536- try:
4537- output = subprocess.check_output(
4538- ['service', service, 'status'],
4539- stderr=subprocess.STDOUT).decode('UTF-8')
4540- except subprocess.CalledProcessError:
4541- return False
4542- else:
4543- if ("start/running" in output or "is running" in output):
4544- return True
4545- else:
4546- return False
4547-
4548-
4549-def service_available(service_name):
4550- """Determine whether a system service is available"""
4551- try:
4552- subprocess.check_output(
4553- ['service', service_name, 'status'],
4554- stderr=subprocess.STDOUT).decode('UTF-8')
4555- except subprocess.CalledProcessError as e:
4556- return b'unrecognized service' not in e.output
4557- else:
4558- return True
4559-
4560-
4561-def adduser(username, password=None, shell='/bin/bash', system_user=False):
4562- """Add a user to the system"""
4563- try:
4564- user_info = pwd.getpwnam(username)
4565- log('user {0} already exists!'.format(username))
4566- except KeyError:
4567- log('creating user {0}'.format(username))
4568- cmd = ['useradd']
4569- if system_user or password is None:
4570- cmd.append('--system')
4571- else:
4572- cmd.extend([
4573- '--create-home',
4574- '--shell', shell,
4575- '--password', password,
4576- ])
4577- cmd.append(username)
4578- subprocess.check_call(cmd)
4579- user_info = pwd.getpwnam(username)
4580- return user_info
4581-
4582-
4583-def add_group(group_name, system_group=False):
4584- """Add a group to the system"""
4585- try:
4586- group_info = grp.getgrnam(group_name)
4587- log('group {0} already exists!'.format(group_name))
4588- except KeyError:
4589- log('creating group {0}'.format(group_name))
4590- cmd = ['addgroup']
4591- if system_group:
4592- cmd.append('--system')
4593- else:
4594- cmd.extend([
4595- '--group',
4596- ])
4597- cmd.append(group_name)
4598- subprocess.check_call(cmd)
4599- group_info = grp.getgrnam(group_name)
4600- return group_info
4601-
4602-
4603-def add_user_to_group(username, group):
4604- """Add a user to a group"""
4605- cmd = ['gpasswd', '-a', username, group]
4606- log("Adding user {} to group {}".format(username, group))
4607- subprocess.check_call(cmd)
4608-
4609-
4610-def rsync(from_path, to_path, flags='-r', options=None):
4611- """Replicate the contents of a path"""
4612- options = options or ['--delete', '--executability']
4613- cmd = ['/usr/bin/rsync', flags]
4614- cmd.extend(options)
4615- cmd.append(from_path)
4616- cmd.append(to_path)
4617- log(" ".join(cmd))
4618- return subprocess.check_output(cmd).decode('UTF-8').strip()
4619-
4620-
4621-def symlink(source, destination):
4622- """Create a symbolic link"""
4623- log("Symlinking {} as {}".format(source, destination))
4624- cmd = [
4625- 'ln',
4626- '-sf',
4627- source,
4628- destination,
4629- ]
4630- subprocess.check_call(cmd)
4631-
4632-
4633-def mkdir(path, owner='root', group='root', perms=0o555, force=False):
4634- """Create a directory"""
4635- log("Making dir {} {}:{} {:o}".format(path, owner, group,
4636- perms))
4637- uid = pwd.getpwnam(owner).pw_uid
4638- gid = grp.getgrnam(group).gr_gid
4639- realpath = os.path.abspath(path)
4640- path_exists = os.path.exists(realpath)
4641- if path_exists and force:
4642- if not os.path.isdir(realpath):
4643- log("Removing non-directory file {} prior to mkdir()".format(path))
4644- os.unlink(realpath)
4645- os.makedirs(realpath, perms)
4646- elif not path_exists:
4647- os.makedirs(realpath, perms)
4648- os.chown(realpath, uid, gid)
4649- os.chmod(realpath, perms)
4650-
4651-
4652-def write_file(path, content, owner='root', group='root', perms=0o444):
4653- """Create or overwrite a file with the contents of a byte string."""
4654- log("Writing file {} {}:{} {:o}".format(path, owner, group, perms))
4655- uid = pwd.getpwnam(owner).pw_uid
4656- gid = grp.getgrnam(group).gr_gid
4657- with open(path, 'wb') as target:
4658- os.fchown(target.fileno(), uid, gid)
4659- os.fchmod(target.fileno(), perms)
4660- target.write(content)
4661-
4662-
4663-def fstab_remove(mp):
4664- """Remove the given mountpoint entry from /etc/fstab
4665- """
4666- return Fstab.remove_by_mountpoint(mp)
4667-
4668-
4669-def fstab_add(dev, mp, fs, options=None):
4670- """Adds the given device entry to the /etc/fstab file
4671- """
4672- return Fstab.add(dev, mp, fs, options=options)
4673-
4674-
4675-def mount(device, mountpoint, options=None, persist=False, filesystem="ext3"):
4676- """Mount a filesystem at a particular mountpoint"""
4677- cmd_args = ['mount']
4678- if options is not None:
4679- cmd_args.extend(['-o', options])
4680- cmd_args.extend([device, mountpoint])
4681- try:
4682- subprocess.check_output(cmd_args)
4683- except subprocess.CalledProcessError as e:
4684- log('Error mounting {} at {}\n{}'.format(device, mountpoint, e.output))
4685- return False
4686-
4687- if persist:
4688- return fstab_add(device, mountpoint, filesystem, options=options)
4689- return True
4690-
4691-
4692-def umount(mountpoint, persist=False):
4693- """Unmount a filesystem"""
4694- cmd_args = ['umount', mountpoint]
4695- try:
4696- subprocess.check_output(cmd_args)
4697- except subprocess.CalledProcessError as e:
4698- log('Error unmounting {}\n{}'.format(mountpoint, e.output))
4699- return False
4700-
4701- if persist:
4702- return fstab_remove(mountpoint)
4703- return True
4704-
4705-
4706-def mounts():
4707- """Get a list of all mounted volumes as [[mountpoint,device],[...]]"""
4708- with open('/proc/mounts') as f:
4709- # [['/mount/point','/dev/path'],[...]]
4710- system_mounts = [m[1::-1] for m in [l.strip().split()
4711- for l in f.readlines()]]
4712- return system_mounts
4713-
4714-
4715-def file_hash(path, hash_type='md5'):
4716- """
4717- Generate a hash checksum of the contents of 'path' or None if not found.
4718-
4719- :param str hash_type: Any hash alrgorithm supported by :mod:`hashlib`,
4720- such as md5, sha1, sha256, sha512, etc.
4721- """
4722- if os.path.exists(path):
4723- h = getattr(hashlib, hash_type)()
4724- with open(path, 'rb') as source:
4725- h.update(source.read())
4726- return h.hexdigest()
4727- else:
4728- return None
4729-
4730-
4731-def path_hash(path):
4732- """
4733- Generate a hash checksum of all files matching 'path'. Standard wildcards
4734- like '*' and '?' are supported, see documentation for the 'glob' module for
4735- more information.
4736-
4737- :return: dict: A { filename: hash } dictionary for all matched files.
4738- Empty if none found.
4739- """
4740- return {
4741- filename: file_hash(filename)
4742- for filename in glob.iglob(path)
4743- }
4744-
4745-
4746-def check_hash(path, checksum, hash_type='md5'):
4747- """
4748- Validate a file using a cryptographic checksum.
4749-
4750- :param str checksum: Value of the checksum used to validate the file.
4751- :param str hash_type: Hash algorithm used to generate `checksum`.
4752- Can be any hash alrgorithm supported by :mod:`hashlib`,
4753- such as md5, sha1, sha256, sha512, etc.
4754- :raises ChecksumError: If the file fails the checksum
4755-
4756- """
4757- actual_checksum = file_hash(path, hash_type)
4758- if checksum != actual_checksum:
4759- raise ChecksumError("'%s' != '%s'" % (checksum, actual_checksum))
4760-
4761-
4762-class ChecksumError(ValueError):
4763- pass
4764-
4765-
4766-def restart_on_change(restart_map, stopstart=False):
4767- """Restart services based on configuration files changing
4768-
4769- This function is used a decorator, for example::
4770-
4771- @restart_on_change({
4772- '/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ]
4773- '/etc/apache/sites-enabled/*': [ 'apache2' ]
4774- })
4775- def config_changed():
4776- pass # your code here
4777-
4778- In this example, the cinder-api and cinder-volume services
4779- would be restarted if /etc/ceph/ceph.conf is changed by the
4780- ceph_client_changed function. The apache2 service would be
4781- restarted if any file matching the pattern got changed, created
4782- or removed. Standard wildcards are supported, see documentation
4783- for the 'glob' module for more information.
4784- """
4785- def wrap(f):
4786- def wrapped_f(*args, **kwargs):
4787- checksums = {path: path_hash(path) for path in restart_map}
4788- f(*args, **kwargs)
4789- restarts = []
4790- for path in restart_map:
4791- if path_hash(path) != checksums[path]:
4792- restarts += restart_map[path]
4793- services_list = list(OrderedDict.fromkeys(restarts))
4794- if not stopstart:
4795- for service_name in services_list:
4796- service('restart', service_name)
4797- else:
4798- for action in ['stop', 'start']:
4799- for service_name in services_list:
4800- service(action, service_name)
4801- return wrapped_f
4802- return wrap
4803-
4804-
4805-def lsb_release():
4806- """Return /etc/lsb-release in a dict"""
4807- d = {}
4808- with open('/etc/lsb-release', 'r') as lsb:
4809- for l in lsb:
4810- k, v = l.split('=')
4811- d[k.strip()] = v.strip()
4812- return d
4813-
4814-
4815-def pwgen(length=None):
4816- """Generate a random pasword."""
4817- if length is None:
4818- # A random length is ok to use a weak PRNG
4819- length = random.choice(range(35, 45))
4820- alphanumeric_chars = [
4821- l for l in (string.ascii_letters + string.digits)
4822- if l not in 'l0QD1vAEIOUaeiou']
4823- # Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the
4824- # actual password
4825- random_generator = random.SystemRandom()
4826- random_chars = [
4827- random_generator.choice(alphanumeric_chars) for _ in range(length)]
4828- return(''.join(random_chars))
4829-
4830-
4831-def list_nics(nic_type):
4832- '''Return a list of nics of given type(s)'''
4833- if isinstance(nic_type, six.string_types):
4834- int_types = [nic_type]
4835- else:
4836- int_types = nic_type
4837- interfaces = []
4838- for int_type in int_types:
4839- cmd = ['ip', 'addr', 'show', 'label', int_type + '*']
4840- ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
4841- ip_output = (line for line in ip_output if line)
4842- for line in ip_output:
4843- if line.split()[1].startswith(int_type):
4844- matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line)
4845- if matched:
4846- interface = matched.groups()[0]
4847- else:
4848- interface = line.split()[1].replace(":", "")
4849- interfaces.append(interface)
4850-
4851- return interfaces
4852-
4853-
4854-def set_nic_mtu(nic, mtu):
4855- '''Set MTU on a network interface'''
4856- cmd = ['ip', 'link', 'set', nic, 'mtu', mtu]
4857- subprocess.check_call(cmd)
4858-
4859-
4860-def get_nic_mtu(nic):
4861- cmd = ['ip', 'addr', 'show', nic]
4862- ip_output = subprocess.check_output(cmd).decode('UTF-8').split('\n')
4863- mtu = ""
4864- for line in ip_output:
4865- words = line.split()
4866- if 'mtu' in words:
4867- mtu = words[words.index("mtu") + 1]
4868- return mtu
4869-
4870-
4871-def get_nic_hwaddr(nic):
4872- cmd = ['ip', '-o', '-0', 'addr', 'show', nic]
4873- ip_output = subprocess.check_output(cmd).decode('UTF-8')
4874- hwaddr = ""
4875- words = ip_output.split()
4876- if 'link/ether' in words:
4877- hwaddr = words[words.index('link/ether') + 1]
4878- return hwaddr
4879-
4880-
4881-def cmp_pkgrevno(package, revno, pkgcache=None):
4882- '''Compare supplied revno with the revno of the installed package
4883-
4884- * 1 => Installed revno is greater than supplied arg
4885- * 0 => Installed revno is the same as supplied arg
4886- * -1 => Installed revno is less than supplied arg
4887-
4888- This function imports apt_cache function from charmhelpers.fetch if
4889- the pkgcache argument is None. Be sure to add charmhelpers.fetch if
4890- you call this function, or pass an apt_pkg.Cache() instance.
4891- '''
4892- import apt_pkg
4893- if not pkgcache:
4894- from charmhelpers.fetch import apt_cache
4895- pkgcache = apt_cache()
4896- pkg = pkgcache[package]
4897- return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
4898-
4899-
4900-@contextmanager
4901-def chdir(d):
4902- cur = os.getcwd()
4903- try:
4904- yield os.chdir(d)
4905- finally:
4906- os.chdir(cur)
4907-
4908-
4909-def chownr(path, owner, group, follow_links=True):
4910- uid = pwd.getpwnam(owner).pw_uid
4911- gid = grp.getgrnam(group).gr_gid
4912- if follow_links:
4913- chown = os.chown
4914- else:
4915- chown = os.lchown
4916-
4917- for root, dirs, files in os.walk(path):
4918- for name in dirs + files:
4919- full = os.path.join(root, name)
4920- broken_symlink = os.path.lexists(full) and not os.path.exists(full)
4921- if not broken_symlink:
4922- chown(full, uid, gid)
4923-
4924-
4925-def lchownr(path, owner, group):
4926- chownr(path, owner, group, follow_links=False)
4927-
4928-
4929-def get_total_ram():
4930- '''The total amount of system RAM in bytes.
4931-
4932- This is what is reported by the OS, and may be overcommitted when
4933- there are multiple containers hosted on the same machine.
4934- '''
4935- with open('/proc/meminfo', 'r') as f:
4936- for line in f.readlines():
4937- if line:
4938- key, value, unit = line.split()
4939- if key == 'MemTotal:':
4940- assert unit == 'kB', 'Unknown unit'
4941- return int(value) * 1024 # Classic, not KiB.
4942- raise NotImplementedError()
4943
4944=== removed directory 'hooks/charmhelpers/core/services'
4945=== removed file 'hooks/charmhelpers/core/services/__init__.py'
4946--- hooks/charmhelpers/core/services/__init__.py 2015-06-26 10:00:19 +0000
4947+++ hooks/charmhelpers/core/services/__init__.py 1970-01-01 00:00:00 +0000
4948@@ -1,18 +0,0 @@
4949-# Copyright 2014-2015 Canonical Limited.
4950-#
4951-# This file is part of charm-helpers.
4952-#
4953-# charm-helpers is free software: you can redistribute it and/or modify
4954-# it under the terms of the GNU Lesser General Public License version 3 as
4955-# published by the Free Software Foundation.
4956-#
4957-# charm-helpers is distributed in the hope that it will be useful,
4958-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4959-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4960-# GNU Lesser General Public License for more details.
4961-#
4962-# You should have received a copy of the GNU Lesser General Public License
4963-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
4964-
4965-from .base import * # NOQA
4966-from .helpers import * # NOQA
4967
4968=== removed file 'hooks/charmhelpers/core/services/base.py'
4969--- hooks/charmhelpers/core/services/base.py 2015-06-26 10:00:19 +0000
4970+++ hooks/charmhelpers/core/services/base.py 1970-01-01 00:00:00 +0000
4971@@ -1,353 +0,0 @@
4972-# Copyright 2014-2015 Canonical Limited.
4973-#
4974-# This file is part of charm-helpers.
4975-#
4976-# charm-helpers is free software: you can redistribute it and/or modify
4977-# it under the terms of the GNU Lesser General Public License version 3 as
4978-# published by the Free Software Foundation.
4979-#
4980-# charm-helpers is distributed in the hope that it will be useful,
4981-# but WITHOUT ANY WARRANTY; without even the implied warranty of
4982-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4983-# GNU Lesser General Public License for more details.
4984-#
4985-# You should have received a copy of the GNU Lesser General Public License
4986-# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
4987-
4988-import os
4989-import json
4990-from inspect import getargspec
4991-from collections import Iterable, OrderedDict
4992-
4993-from charmhelpers.core import host
4994-from charmhelpers.core import hookenv
4995-
4996-
4997-__all__ = ['ServiceManager', 'ManagerCallback',
4998- 'PortManagerCallback', 'open_ports', 'close_ports', 'manage_ports',
4999- 'service_restart', 'service_stop']
5000-
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: