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 on 2016-05-24 |
| 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 on 2016-04-29 | |
| charmers | 2016-01-14 | Pending | |
|
Review via email:
|
|||
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 on 2016-02-04
-
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 on 2016-02-05
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 157. By Stuart Bishop on 2016-02-06
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 158. By Stuart Bishop on 2016-02-08
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 159. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 160. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 161. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 162. By Stuart Bishop on 2016-02-11
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 163. By Stuart Bishop on 2016-02-15
-
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 on 2016-02-23
-
Merge trunk, resolve conflicts
- 165. By Stuart Bishop on 2016-02-24
-
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 on 2016-03-17
-
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 on 2016-03-25
-
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 on 2016-03-25
-
Ignore .tox directory when tests repackage the charm
- 166. By Stuart Bishop on 2016-03-17
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: master - 165. By Stuart Bishop on 2016-02-24
-
delint, per trunk
- 164. By Stuart Bishop on 2016-02-23
-
Merge trunk, resolve conflicts
- 163. By Stuart Bishop on 2016-02-15
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 162. By Stuart Bishop on 2016-02-11
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 161. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 160. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 159. By Stuart Bishop on 2016-02-10
-
charm build of git+ssh://<email address hidden>
/~stub/ charms/ +source/ postgresql: layered - 158. By Stuart Bishop on 2016-02-08
-
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.