Merge lp:~stub/charms/trusty/postgresql/built into lp:charms/trusty/postgresql
- Trusty Tahr (14.04)
- built
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Review Queue (community) | automated testing | Needs Fixing | |
charmers | Pending | ||
Review via email: mp+282588@code.launchpad.net |
Commit message
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:/
The pure reactive rework (without layer support) is at https:/
A mirror of the current PostgreSQL charm trunk is at https:/
All three branches are in the same repository at lp:~stub/charms/+source/postgresql (or git+ssh:
At least three layers will be broken out of this work (reactive/
Stuart Bishop (stub) wrote : | # |
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 155. By Stuart Bishop
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 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
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 164. By Stuart Bishop
-
Merge trunk, resolve conflicts
- 165. By Stuart Bishop
-
delint, per trunk
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 166. By Stuart Bishop
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: master
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)
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
The results (PASS) are in and available here: http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
- 167. By Stuart Bishop
-
Ignore .tox directory when tests repackage the charm
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
Review Queue (review-queue) wrote : | # |
This item has failed automated testing! Results available here http://
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
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 | - |
This work also added PostgreSQL 9.5 support.