Merge ~james-page/ntpmaster-charm:add-update-status into ntpmaster-charm:master
- Git
- lp:~james-page/ntpmaster-charm
- add-update-status
- Merge into master
Proposed by
James Page
Status: | Merged |
---|---|
Merged at revision: | f26da95f35edf50c95c7598f01887e2a9d7e44ef |
Proposed branch: | ~james-page/ntpmaster-charm:add-update-status |
Merge into: | ntpmaster-charm:master |
Diff against target: |
2260 lines (+1055/-624) 33 files modified
.gitignore (+1/-0) charm-helpers-sync.yaml (+1/-0) hooks/charmhelpers/__init__.py (+9/-11) hooks/charmhelpers/core/__init__.py (+9/-11) hooks/charmhelpers/core/decorators.py (+9/-11) hooks/charmhelpers/core/files.py (+9/-11) hooks/charmhelpers/core/fstab.py (+9/-11) hooks/charmhelpers/core/hookenv.py (+24/-12) hooks/charmhelpers/core/host.py (+105/-88) hooks/charmhelpers/core/host_factory/__init__.py (+0/-0) hooks/charmhelpers/core/host_factory/centos.py (+56/-0) hooks/charmhelpers/core/host_factory/ubuntu.py (+56/-0) hooks/charmhelpers/core/hugepage.py (+9/-11) hooks/charmhelpers/core/kernel.py (+30/-26) hooks/charmhelpers/core/kernel_factory/__init__.py (+0/-0) hooks/charmhelpers/core/kernel_factory/centos.py (+17/-0) hooks/charmhelpers/core/kernel_factory/ubuntu.py (+13/-0) hooks/charmhelpers/core/services/__init__.py (+9/-11) hooks/charmhelpers/core/services/base.py (+9/-11) hooks/charmhelpers/core/services/helpers.py (+9/-11) hooks/charmhelpers/core/strutils.py (+9/-11) hooks/charmhelpers/core/sysctl.py (+9/-11) hooks/charmhelpers/core/templating.py (+17/-14) hooks/charmhelpers/core/unitdata.py (+9/-12) hooks/charmhelpers/fetch/__init__.py (+38/-305) hooks/charmhelpers/fetch/archiveurl.py (+9/-11) hooks/charmhelpers/fetch/bzrurl.py (+29/-21) hooks/charmhelpers/fetch/centos.py (+171/-0) hooks/charmhelpers/fetch/giturl.py (+13/-14) hooks/charmhelpers/fetch/ubuntu.py (+336/-0) hooks/charmhelpers/osplatform.py (+19/-0) hooks/ntpmaster_hooks.py (+11/-0) hooks/update-status (+1/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stuart Bishop (community) | Approve | ||
Review via email: mp+308712@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
James Page (james-page) wrote : | # |
Revision history for this message
Stuart Bishop (stub) wrote : | # |
Almost entirely charmhelpers sync.
The new hooks looks good and should do what it says on the tin.
I'm not here this week so somebody else gets to land this :)
review:
Approve
Revision history for this message
Paul Gear (paulgear) wrote : | # |
Merged; question about future use of status_set() in https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.gitignore b/.gitignore | |||
2 | 0 | new file mode 100644 | 0 | new file mode 100644 |
3 | index 0000000..ba077a4 | |||
4 | --- /dev/null | |||
5 | +++ b/.gitignore | |||
6 | @@ -0,0 +1 @@ | |||
7 | 1 | bin | ||
8 | diff --git a/charm-helpers-sync.yaml b/charm-helpers-sync.yaml | |||
9 | index 7fd4ca6..c6dcfa3 100644 | |||
10 | --- a/charm-helpers-sync.yaml | |||
11 | +++ b/charm-helpers-sync.yaml | |||
12 | @@ -1,5 +1,6 @@ | |||
13 | 1 | branch: lp:charm-helpers | 1 | branch: lp:charm-helpers |
14 | 2 | destination: hooks/charmhelpers | 2 | destination: hooks/charmhelpers |
15 | 3 | include: | 3 | include: |
16 | 4 | - osplatform | ||
17 | 4 | - core | 5 | - core |
18 | 5 | - fetch | 6 | - fetch |
19 | diff --git a/hooks/charmhelpers/__init__.py b/hooks/charmhelpers/__init__.py | |||
20 | index f72e7f8..4886788 100644 | |||
21 | --- a/hooks/charmhelpers/__init__.py | |||
22 | +++ b/hooks/charmhelpers/__init__.py | |||
23 | @@ -1,18 +1,16 @@ | |||
24 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
25 | 2 | # | 2 | # |
27 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
28 | 4 | # you may not use this file except in compliance with the License. | ||
29 | 5 | # You may obtain a copy of the License at | ||
30 | 4 | # | 6 | # |
34 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
32 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
33 | 7 | # published by the Free Software Foundation. | ||
35 | 8 | # | 8 | # |
43 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
44 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
45 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
46 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
47 | 13 | # | 13 | # limitations under the License. |
41 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
42 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
48 | 16 | 14 | ||
49 | 17 | # Bootstrap charm-helpers, installing its dependencies if necessary using | 15 | # Bootstrap charm-helpers, installing its dependencies if necessary using |
50 | 18 | # only standard libraries. | 16 | # only standard libraries. |
51 | diff --git a/hooks/charmhelpers/core/__init__.py b/hooks/charmhelpers/core/__init__.py | |||
52 | index d1400a0..d7567b8 100644 | |||
53 | --- a/hooks/charmhelpers/core/__init__.py | |||
54 | +++ b/hooks/charmhelpers/core/__init__.py | |||
55 | @@ -1,15 +1,13 @@ | |||
56 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
57 | 2 | # | 2 | # |
59 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
60 | 4 | # you may not use this file except in compliance with the License. | ||
61 | 5 | # You may obtain a copy of the License at | ||
62 | 4 | # | 6 | # |
66 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
64 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
65 | 7 | # published by the Free Software Foundation. | ||
67 | 8 | # | 8 | # |
75 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
76 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
77 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
78 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
79 | 13 | # | 13 | # limitations under the License. |
73 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
74 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
80 | diff --git a/hooks/charmhelpers/core/decorators.py b/hooks/charmhelpers/core/decorators.py | |||
81 | index bb05620..6ad41ee 100644 | |||
82 | --- a/hooks/charmhelpers/core/decorators.py | |||
83 | +++ b/hooks/charmhelpers/core/decorators.py | |||
84 | @@ -1,18 +1,16 @@ | |||
85 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
86 | 2 | # | 2 | # |
88 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
89 | 4 | # you may not use this file except in compliance with the License. | ||
90 | 5 | # You may obtain a copy of the License at | ||
91 | 4 | # | 6 | # |
95 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
93 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
94 | 7 | # published by the Free Software Foundation. | ||
96 | 8 | # | 8 | # |
104 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
105 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
106 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
107 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
108 | 13 | # | 13 | # limitations under the License. |
102 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
103 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
109 | 16 | 14 | ||
110 | 17 | # | 15 | # |
111 | 18 | # Copyright 2014 Canonical Ltd. | 16 | # Copyright 2014 Canonical Ltd. |
112 | diff --git a/hooks/charmhelpers/core/files.py b/hooks/charmhelpers/core/files.py | |||
113 | index 0f12d32..fdd82b7 100644 | |||
114 | --- a/hooks/charmhelpers/core/files.py | |||
115 | +++ b/hooks/charmhelpers/core/files.py | |||
116 | @@ -3,19 +3,17 @@ | |||
117 | 3 | 3 | ||
118 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
119 | 5 | # | 5 | # |
121 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
122 | 7 | # you may not use this file except in compliance with the License. | ||
123 | 8 | # You may obtain a copy of the License at | ||
124 | 7 | # | 9 | # |
128 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
126 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
127 | 10 | # published by the Free Software Foundation. | ||
129 | 11 | # | 11 | # |
137 | 12 | # charm-helpers is distributed in the hope that it will be useful, | 12 | # Unless required by applicable law or agreed to in writing, software |
138 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
139 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
140 | 15 | # GNU Lesser General Public License for more details. | 15 | # See the License for the specific language governing permissions and |
141 | 16 | # | 16 | # limitations under the License. |
135 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
136 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
142 | 19 | 17 | ||
143 | 20 | __author__ = 'Jorge Niedbalski <niedbalski@ubuntu.com>' | 18 | __author__ = 'Jorge Niedbalski <niedbalski@ubuntu.com>' |
144 | 21 | 19 | ||
145 | diff --git a/hooks/charmhelpers/core/fstab.py b/hooks/charmhelpers/core/fstab.py | |||
146 | index 3056fba..d9fa915 100644 | |||
147 | --- a/hooks/charmhelpers/core/fstab.py | |||
148 | +++ b/hooks/charmhelpers/core/fstab.py | |||
149 | @@ -3,19 +3,17 @@ | |||
150 | 3 | 3 | ||
151 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
152 | 5 | # | 5 | # |
154 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
155 | 7 | # you may not use this file except in compliance with the License. | ||
156 | 8 | # You may obtain a copy of the License at | ||
157 | 7 | # | 9 | # |
161 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
159 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
160 | 10 | # published by the Free Software Foundation. | ||
162 | 11 | # | 11 | # |
170 | 12 | # charm-helpers is distributed in the hope that it will be useful, | 12 | # Unless required by applicable law or agreed to in writing, software |
171 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
172 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
173 | 15 | # GNU Lesser General Public License for more details. | 15 | # See the License for the specific language governing permissions and |
174 | 16 | # | 16 | # limitations under the License. |
168 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
169 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
175 | 19 | 17 | ||
176 | 20 | import io | 18 | import io |
177 | 21 | import os | 19 | import os |
178 | diff --git a/hooks/charmhelpers/core/hookenv.py b/hooks/charmhelpers/core/hookenv.py | |||
179 | index 0132129..996e81c 100644 | |||
180 | --- a/hooks/charmhelpers/core/hookenv.py | |||
181 | +++ b/hooks/charmhelpers/core/hookenv.py | |||
182 | @@ -1,18 +1,16 @@ | |||
183 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
184 | 2 | # | 2 | # |
186 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
187 | 4 | # you may not use this file except in compliance with the License. | ||
188 | 5 | # You may obtain a copy of the License at | ||
189 | 4 | # | 6 | # |
193 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
191 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
192 | 7 | # published by the Free Software Foundation. | ||
194 | 8 | # | 8 | # |
202 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
203 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
204 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
205 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
206 | 13 | # | 13 | # limitations under the License. |
200 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
201 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
207 | 16 | 14 | ||
208 | 17 | "Interactions with the Juju environment" | 15 | "Interactions with the Juju environment" |
209 | 18 | # Copyright 2013 Canonical Ltd. | 16 | # Copyright 2013 Canonical Ltd. |
210 | @@ -845,6 +843,20 @@ def translate_exc(from_exc, to_exc): | |||
211 | 845 | return inner_translate_exc1 | 843 | return inner_translate_exc1 |
212 | 846 | 844 | ||
213 | 847 | 845 | ||
214 | 846 | def application_version_set(version): | ||
215 | 847 | """Charm authors may trigger this command from any hook to output what | ||
216 | 848 | version of the application is running. This could be a package version, | ||
217 | 849 | for instance postgres version 9.5. It could also be a build number or | ||
218 | 850 | version control revision identifier, for instance git sha 6fb7ba68. """ | ||
219 | 851 | |||
220 | 852 | cmd = ['application-version-set'] | ||
221 | 853 | cmd.append(version) | ||
222 | 854 | try: | ||
223 | 855 | subprocess.check_call(cmd) | ||
224 | 856 | except OSError: | ||
225 | 857 | log("Application Version: {}".format(version)) | ||
226 | 858 | |||
227 | 859 | |||
228 | 848 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) | 860 | @translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
229 | 849 | def is_leader(): | 861 | def is_leader(): |
230 | 850 | """Does the current unit hold the juju leadership | 862 | """Does the current unit hold the juju leadership |
231 | @@ -1006,4 +1018,4 @@ def network_get_primary_address(binding): | |||
232 | 1006 | :raise: NotImplementedError if run on Juju < 2.0 | 1018 | :raise: NotImplementedError if run on Juju < 2.0 |
233 | 1007 | ''' | 1019 | ''' |
234 | 1008 | cmd = ['network-get', '--primary-address', binding] | 1020 | cmd = ['network-get', '--primary-address', binding] |
236 | 1009 | return subprocess.check_output(cmd).strip() | 1021 | return subprocess.check_output(cmd).decode('UTF-8').strip() |
237 | diff --git a/hooks/charmhelpers/core/host.py b/hooks/charmhelpers/core/host.py | |||
238 | index bfea6a1..0f1b2f3 100644 | |||
239 | --- a/hooks/charmhelpers/core/host.py | |||
240 | +++ b/hooks/charmhelpers/core/host.py | |||
241 | @@ -1,18 +1,16 @@ | |||
242 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
243 | 2 | # | 2 | # |
245 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
246 | 4 | # you may not use this file except in compliance with the License. | ||
247 | 5 | # You may obtain a copy of the License at | ||
248 | 4 | # | 6 | # |
252 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
250 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
251 | 7 | # published by the Free Software Foundation. | ||
253 | 8 | # | 8 | # |
261 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
262 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
263 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
264 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
265 | 13 | # | 13 | # limitations under the License. |
259 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
260 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
266 | 16 | 14 | ||
267 | 17 | """Tools for working with the host system""" | 15 | """Tools for working with the host system""" |
268 | 18 | # Copyright 2012 Canonical Ltd. | 16 | # Copyright 2012 Canonical Ltd. |
269 | @@ -32,13 +30,29 @@ import subprocess | |||
270 | 32 | import hashlib | 30 | import hashlib |
271 | 33 | import functools | 31 | import functools |
272 | 34 | import itertools | 32 | import itertools |
273 | 35 | from contextlib import contextmanager | ||
274 | 36 | from collections import OrderedDict | ||
275 | 37 | |||
276 | 38 | import six | 33 | import six |
277 | 39 | 34 | ||
278 | 35 | from contextlib import contextmanager | ||
279 | 36 | from collections import OrderedDict | ||
280 | 40 | from .hookenv import log | 37 | from .hookenv import log |
281 | 41 | from .fstab import Fstab | 38 | from .fstab import Fstab |
282 | 39 | from charmhelpers.osplatform import get_platform | ||
283 | 40 | |||
284 | 41 | __platform__ = get_platform() | ||
285 | 42 | if __platform__ == "ubuntu": | ||
286 | 43 | from charmhelpers.core.host_factory.ubuntu import ( | ||
287 | 44 | service_available, | ||
288 | 45 | add_new_group, | ||
289 | 46 | lsb_release, | ||
290 | 47 | cmp_pkgrevno, | ||
291 | 48 | ) # flake8: noqa -- ignore F401 for this import | ||
292 | 49 | elif __platform__ == "centos": | ||
293 | 50 | from charmhelpers.core.host_factory.centos import ( | ||
294 | 51 | service_available, | ||
295 | 52 | add_new_group, | ||
296 | 53 | lsb_release, | ||
297 | 54 | cmp_pkgrevno, | ||
298 | 55 | ) # flake8: noqa -- ignore F401 for this import | ||
299 | 42 | 56 | ||
300 | 43 | 57 | ||
301 | 44 | def service_start(service_name): | 58 | def service_start(service_name): |
302 | @@ -128,11 +142,8 @@ def service(action, service_name): | |||
303 | 128 | return subprocess.call(cmd) == 0 | 142 | return subprocess.call(cmd) == 0 |
304 | 129 | 143 | ||
305 | 130 | 144 | ||
311 | 131 | def systemv_services_running(): | 145 | _UPSTART_CONF = "/etc/init/{}.conf" |
312 | 132 | output = subprocess.check_output( | 146 | _INIT_D_CONF = "/etc/init.d/{}" |
308 | 133 | ['service', '--status-all'], | ||
309 | 134 | stderr=subprocess.STDOUT).decode('UTF-8') | ||
310 | 135 | return [row.split()[-1] for row in output.split('\n') if '[ + ]' in row] | ||
313 | 136 | 147 | ||
314 | 137 | 148 | ||
315 | 138 | def service_running(service_name): | 149 | def service_running(service_name): |
316 | @@ -140,34 +151,25 @@ def service_running(service_name): | |||
317 | 140 | if init_is_systemd(): | 151 | if init_is_systemd(): |
318 | 141 | return service('is-active', service_name) | 152 | return service('is-active', service_name) |
319 | 142 | else: | 153 | else: |
332 | 143 | try: | 154 | if os.path.exists(_UPSTART_CONF.format(service_name)): |
333 | 144 | output = subprocess.check_output( | 155 | try: |
334 | 145 | ['service', service_name, 'status'], | 156 | output = subprocess.check_output( |
335 | 146 | stderr=subprocess.STDOUT).decode('UTF-8') | 157 | ['status', service_name], |
336 | 147 | except subprocess.CalledProcessError: | 158 | stderr=subprocess.STDOUT).decode('UTF-8') |
337 | 148 | return False | 159 | except subprocess.CalledProcessError: |
338 | 149 | else: | 160 | return False |
339 | 150 | # This works for upstart scripts where the 'service' command | 161 | else: |
340 | 151 | # returns a consistent string to represent running 'start/running' | 162 | # This works for upstart scripts where the 'service' command |
341 | 152 | if ("start/running" in output or "is running" in output or | 163 | # returns a consistent string to represent running |
342 | 153 | "up and running" in output): | 164 | # 'start/running' |
343 | 154 | return True | 165 | if ("start/running" in output or |
344 | 166 | "is running" in output or | ||
345 | 167 | "up and running" in output): | ||
346 | 168 | return True | ||
347 | 169 | elif os.path.exists(_INIT_D_CONF.format(service_name)): | ||
348 | 155 | # Check System V scripts init script return codes | 170 | # Check System V scripts init script return codes |
364 | 156 | if service_name in systemv_services_running(): | 171 | return service('status', service_name) |
365 | 157 | return True | 172 | return False |
351 | 158 | return False | ||
352 | 159 | |||
353 | 160 | |||
354 | 161 | def service_available(service_name): | ||
355 | 162 | """Determine whether a system service is available""" | ||
356 | 163 | try: | ||
357 | 164 | subprocess.check_output( | ||
358 | 165 | ['service', service_name, 'status'], | ||
359 | 166 | stderr=subprocess.STDOUT).decode('UTF-8') | ||
360 | 167 | except subprocess.CalledProcessError as e: | ||
361 | 168 | return b'unrecognized service' not in e.output | ||
362 | 169 | else: | ||
363 | 170 | return True | ||
366 | 171 | 173 | ||
367 | 172 | 174 | ||
368 | 173 | SYSTEMD_SYSTEM = '/run/systemd/system' | 175 | SYSTEMD_SYSTEM = '/run/systemd/system' |
369 | @@ -178,8 +180,9 @@ def init_is_systemd(): | |||
370 | 178 | return os.path.isdir(SYSTEMD_SYSTEM) | 180 | return os.path.isdir(SYSTEMD_SYSTEM) |
371 | 179 | 181 | ||
372 | 180 | 182 | ||
375 | 181 | def adduser(username, password=None, shell='/bin/bash', system_user=False, | 183 | def adduser(username, password=None, shell='/bin/bash', |
376 | 182 | primary_group=None, secondary_groups=None): | 184 | system_user=False, primary_group=None, |
377 | 185 | secondary_groups=None, uid=None, home_dir=None): | ||
378 | 183 | """Add a user to the system. | 186 | """Add a user to the system. |
379 | 184 | 187 | ||
380 | 185 | Will log but otherwise succeed if the user already exists. | 188 | Will log but otherwise succeed if the user already exists. |
381 | @@ -190,15 +193,24 @@ def adduser(username, password=None, shell='/bin/bash', system_user=False, | |||
382 | 190 | :param bool system_user: Whether to create a login or system user | 193 | :param bool system_user: Whether to create a login or system user |
383 | 191 | :param str primary_group: Primary group for user; defaults to username | 194 | :param str primary_group: Primary group for user; defaults to username |
384 | 192 | :param list secondary_groups: Optional list of additional groups | 195 | :param list secondary_groups: Optional list of additional groups |
385 | 196 | :param int uid: UID for user being created | ||
386 | 197 | :param str home_dir: Home directory for user | ||
387 | 193 | 198 | ||
388 | 194 | :returns: The password database entry struct, as returned by `pwd.getpwnam` | 199 | :returns: The password database entry struct, as returned by `pwd.getpwnam` |
389 | 195 | """ | 200 | """ |
390 | 196 | try: | 201 | try: |
391 | 197 | user_info = pwd.getpwnam(username) | 202 | user_info = pwd.getpwnam(username) |
392 | 198 | log('user {0} already exists!'.format(username)) | 203 | log('user {0} already exists!'.format(username)) |
393 | 204 | if uid: | ||
394 | 205 | user_info = pwd.getpwuid(int(uid)) | ||
395 | 206 | log('user with uid {0} already exists!'.format(uid)) | ||
396 | 199 | except KeyError: | 207 | except KeyError: |
397 | 200 | log('creating user {0}'.format(username)) | 208 | log('creating user {0}'.format(username)) |
398 | 201 | cmd = ['useradd'] | 209 | cmd = ['useradd'] |
399 | 210 | if uid: | ||
400 | 211 | cmd.extend(['--uid', str(uid)]) | ||
401 | 212 | if home_dir: | ||
402 | 213 | cmd.extend(['--home', str(home_dir)]) | ||
403 | 202 | if system_user or password is None: | 214 | if system_user or password is None: |
404 | 203 | cmd.append('--system') | 215 | cmd.append('--system') |
405 | 204 | else: | 216 | else: |
406 | @@ -233,22 +245,56 @@ def user_exists(username): | |||
407 | 233 | return user_exists | 245 | return user_exists |
408 | 234 | 246 | ||
409 | 235 | 247 | ||
412 | 236 | def add_group(group_name, system_group=False): | 248 | def uid_exists(uid): |
413 | 237 | """Add a group to the system""" | 249 | """Check if a uid exists""" |
414 | 250 | try: | ||
415 | 251 | pwd.getpwuid(uid) | ||
416 | 252 | uid_exists = True | ||
417 | 253 | except KeyError: | ||
418 | 254 | uid_exists = False | ||
419 | 255 | return uid_exists | ||
420 | 256 | |||
421 | 257 | |||
422 | 258 | def group_exists(groupname): | ||
423 | 259 | """Check if a group exists""" | ||
424 | 260 | try: | ||
425 | 261 | grp.getgrnam(groupname) | ||
426 | 262 | group_exists = True | ||
427 | 263 | except KeyError: | ||
428 | 264 | group_exists = False | ||
429 | 265 | return group_exists | ||
430 | 266 | |||
431 | 267 | |||
432 | 268 | def gid_exists(gid): | ||
433 | 269 | """Check if a gid exists""" | ||
434 | 270 | try: | ||
435 | 271 | grp.getgrgid(gid) | ||
436 | 272 | gid_exists = True | ||
437 | 273 | except KeyError: | ||
438 | 274 | gid_exists = False | ||
439 | 275 | return gid_exists | ||
440 | 276 | |||
441 | 277 | |||
442 | 278 | def add_group(group_name, system_group=False, gid=None): | ||
443 | 279 | """Add a group to the system | ||
444 | 280 | |||
445 | 281 | Will log but otherwise succeed if the group already exists. | ||
446 | 282 | |||
447 | 283 | :param str group_name: group to create | ||
448 | 284 | :param bool system_group: Create system group | ||
449 | 285 | :param int gid: GID for user being created | ||
450 | 286 | |||
451 | 287 | :returns: The password database entry struct, as returned by `grp.getgrnam` | ||
452 | 288 | """ | ||
453 | 238 | try: | 289 | try: |
454 | 239 | group_info = grp.getgrnam(group_name) | 290 | group_info = grp.getgrnam(group_name) |
455 | 240 | log('group {0} already exists!'.format(group_name)) | 291 | log('group {0} already exists!'.format(group_name)) |
456 | 292 | if gid: | ||
457 | 293 | group_info = grp.getgrgid(gid) | ||
458 | 294 | log('group with gid {0} already exists!'.format(gid)) | ||
459 | 241 | except KeyError: | 295 | except KeyError: |
460 | 242 | log('creating group {0}'.format(group_name)) | 296 | log('creating group {0}'.format(group_name)) |
470 | 243 | cmd = ['addgroup'] | 297 | add_new_group(group_name, system_group, gid) |
462 | 244 | if system_group: | ||
463 | 245 | cmd.append('--system') | ||
464 | 246 | else: | ||
465 | 247 | cmd.extend([ | ||
466 | 248 | '--group', | ||
467 | 249 | ]) | ||
468 | 250 | cmd.append(group_name) | ||
469 | 251 | subprocess.check_call(cmd) | ||
471 | 252 | group_info = grp.getgrnam(group_name) | 298 | group_info = grp.getgrnam(group_name) |
472 | 253 | return group_info | 299 | return group_info |
473 | 254 | 300 | ||
474 | @@ -493,16 +539,6 @@ def restart_on_change_helper(lambda_f, restart_map, stopstart=False, | |||
475 | 493 | return r | 539 | return r |
476 | 494 | 540 | ||
477 | 495 | 541 | ||
478 | 496 | def lsb_release(): | ||
479 | 497 | """Return /etc/lsb-release in a dict""" | ||
480 | 498 | d = {} | ||
481 | 499 | with open('/etc/lsb-release', 'r') as lsb: | ||
482 | 500 | for l in lsb: | ||
483 | 501 | k, v = l.split('=') | ||
484 | 502 | d[k.strip()] = v.strip() | ||
485 | 503 | return d | ||
486 | 504 | |||
487 | 505 | |||
488 | 506 | def pwgen(length=None): | 542 | def pwgen(length=None): |
489 | 507 | """Generate a random pasword.""" | 543 | """Generate a random pasword.""" |
490 | 508 | if length is None: | 544 | if length is None: |
491 | @@ -626,25 +662,6 @@ def get_nic_hwaddr(nic): | |||
492 | 626 | return hwaddr | 662 | return hwaddr |
493 | 627 | 663 | ||
494 | 628 | 664 | ||
495 | 629 | def cmp_pkgrevno(package, revno, pkgcache=None): | ||
496 | 630 | """Compare supplied revno with the revno of the installed package | ||
497 | 631 | |||
498 | 632 | * 1 => Installed revno is greater than supplied arg | ||
499 | 633 | * 0 => Installed revno is the same as supplied arg | ||
500 | 634 | * -1 => Installed revno is less than supplied arg | ||
501 | 635 | |||
502 | 636 | This function imports apt_cache function from charmhelpers.fetch if | ||
503 | 637 | the pkgcache argument is None. Be sure to add charmhelpers.fetch if | ||
504 | 638 | you call this function, or pass an apt_pkg.Cache() instance. | ||
505 | 639 | """ | ||
506 | 640 | import apt_pkg | ||
507 | 641 | if not pkgcache: | ||
508 | 642 | from charmhelpers.fetch import apt_cache | ||
509 | 643 | pkgcache = apt_cache() | ||
510 | 644 | pkg = pkgcache[package] | ||
511 | 645 | return apt_pkg.version_compare(pkg.current_ver.ver_str, revno) | ||
512 | 646 | |||
513 | 647 | |||
514 | 648 | @contextmanager | 665 | @contextmanager |
515 | 649 | def chdir(directory): | 666 | def chdir(directory): |
516 | 650 | """Change the current working directory to a different directory for a code | 667 | """Change the current working directory to a different directory for a code |
517 | diff --git a/hooks/charmhelpers/core/host_factory/__init__.py b/hooks/charmhelpers/core/host_factory/__init__.py | |||
518 | 651 | new file mode 100644 | 668 | new file mode 100644 |
519 | index 0000000..e69de29 | |||
520 | --- /dev/null | |||
521 | +++ b/hooks/charmhelpers/core/host_factory/__init__.py | |||
522 | diff --git a/hooks/charmhelpers/core/host_factory/centos.py b/hooks/charmhelpers/core/host_factory/centos.py | |||
523 | 652 | new file mode 100644 | 669 | new file mode 100644 |
524 | index 0000000..902d469 | |||
525 | --- /dev/null | |||
526 | +++ b/hooks/charmhelpers/core/host_factory/centos.py | |||
527 | @@ -0,0 +1,56 @@ | |||
528 | 1 | import subprocess | ||
529 | 2 | import yum | ||
530 | 3 | import os | ||
531 | 4 | |||
532 | 5 | |||
533 | 6 | def service_available(service_name): | ||
534 | 7 | # """Determine whether a system service is available.""" | ||
535 | 8 | if os.path.isdir('/run/systemd/system'): | ||
536 | 9 | cmd = ['systemctl', 'is-enabled', service_name] | ||
537 | 10 | else: | ||
538 | 11 | cmd = ['service', service_name, 'is-enabled'] | ||
539 | 12 | return subprocess.call(cmd) == 0 | ||
540 | 13 | |||
541 | 14 | |||
542 | 15 | def add_new_group(group_name, system_group=False, gid=None): | ||
543 | 16 | cmd = ['groupadd'] | ||
544 | 17 | if gid: | ||
545 | 18 | cmd.extend(['--gid', str(gid)]) | ||
546 | 19 | if system_group: | ||
547 | 20 | cmd.append('-r') | ||
548 | 21 | cmd.append(group_name) | ||
549 | 22 | subprocess.check_call(cmd) | ||
550 | 23 | |||
551 | 24 | |||
552 | 25 | def lsb_release(): | ||
553 | 26 | """Return /etc/os-release in a dict.""" | ||
554 | 27 | d = {} | ||
555 | 28 | with open('/etc/os-release', 'r') as lsb: | ||
556 | 29 | for l in lsb: | ||
557 | 30 | s = l.split('=') | ||
558 | 31 | if len(s) != 2: | ||
559 | 32 | continue | ||
560 | 33 | d[s[0].strip()] = s[1].strip() | ||
561 | 34 | return d | ||
562 | 35 | |||
563 | 36 | |||
564 | 37 | def cmp_pkgrevno(package, revno, pkgcache=None): | ||
565 | 38 | """Compare supplied revno with the revno of the installed package. | ||
566 | 39 | |||
567 | 40 | * 1 => Installed revno is greater than supplied arg | ||
568 | 41 | * 0 => Installed revno is the same as supplied arg | ||
569 | 42 | * -1 => Installed revno is less than supplied arg | ||
570 | 43 | |||
571 | 44 | This function imports YumBase function if the pkgcache argument | ||
572 | 45 | is None. | ||
573 | 46 | """ | ||
574 | 47 | if not pkgcache: | ||
575 | 48 | y = yum.YumBase() | ||
576 | 49 | packages = y.doPackageLists() | ||
577 | 50 | pkgcache = {i.Name: i.version for i in packages['installed']} | ||
578 | 51 | pkg = pkgcache[package] | ||
579 | 52 | if pkg > revno: | ||
580 | 53 | return 1 | ||
581 | 54 | if pkg < revno: | ||
582 | 55 | return -1 | ||
583 | 56 | return 0 | ||
584 | diff --git a/hooks/charmhelpers/core/host_factory/ubuntu.py b/hooks/charmhelpers/core/host_factory/ubuntu.py | |||
585 | 0 | new file mode 100644 | 57 | new file mode 100644 |
586 | index 0000000..8c66af5 | |||
587 | --- /dev/null | |||
588 | +++ b/hooks/charmhelpers/core/host_factory/ubuntu.py | |||
589 | @@ -0,0 +1,56 @@ | |||
590 | 1 | import subprocess | ||
591 | 2 | |||
592 | 3 | |||
593 | 4 | def service_available(service_name): | ||
594 | 5 | """Determine whether a system service is available""" | ||
595 | 6 | try: | ||
596 | 7 | subprocess.check_output( | ||
597 | 8 | ['service', service_name, 'status'], | ||
598 | 9 | stderr=subprocess.STDOUT).decode('UTF-8') | ||
599 | 10 | except subprocess.CalledProcessError as e: | ||
600 | 11 | return b'unrecognized service' not in e.output | ||
601 | 12 | else: | ||
602 | 13 | return True | ||
603 | 14 | |||
604 | 15 | |||
605 | 16 | def add_new_group(group_name, system_group=False, gid=None): | ||
606 | 17 | cmd = ['addgroup'] | ||
607 | 18 | if gid: | ||
608 | 19 | cmd.extend(['--gid', str(gid)]) | ||
609 | 20 | if system_group: | ||
610 | 21 | cmd.append('--system') | ||
611 | 22 | else: | ||
612 | 23 | cmd.extend([ | ||
613 | 24 | '--group', | ||
614 | 25 | ]) | ||
615 | 26 | cmd.append(group_name) | ||
616 | 27 | subprocess.check_call(cmd) | ||
617 | 28 | |||
618 | 29 | |||
619 | 30 | def lsb_release(): | ||
620 | 31 | """Return /etc/lsb-release in a dict""" | ||
621 | 32 | d = {} | ||
622 | 33 | with open('/etc/lsb-release', 'r') as lsb: | ||
623 | 34 | for l in lsb: | ||
624 | 35 | k, v = l.split('=') | ||
625 | 36 | d[k.strip()] = v.strip() | ||
626 | 37 | return d | ||
627 | 38 | |||
628 | 39 | |||
629 | 40 | def cmp_pkgrevno(package, revno, pkgcache=None): | ||
630 | 41 | """Compare supplied revno with the revno of the installed package. | ||
631 | 42 | |||
632 | 43 | * 1 => Installed revno is greater than supplied arg | ||
633 | 44 | * 0 => Installed revno is the same as supplied arg | ||
634 | 45 | * -1 => Installed revno is less than supplied arg | ||
635 | 46 | |||
636 | 47 | This function imports apt_cache function from charmhelpers.fetch if | ||
637 | 48 | the pkgcache argument is None. Be sure to add charmhelpers.fetch if | ||
638 | 49 | you call this function, or pass an apt_pkg.Cache() instance. | ||
639 | 50 | """ | ||
640 | 51 | import apt_pkg | ||
641 | 52 | if not pkgcache: | ||
642 | 53 | from charmhelpers.fetch import apt_cache | ||
643 | 54 | pkgcache = apt_cache() | ||
644 | 55 | pkg = pkgcache[package] | ||
645 | 56 | return apt_pkg.version_compare(pkg.current_ver.ver_str, revno) | ||
646 | diff --git a/hooks/charmhelpers/core/hugepage.py b/hooks/charmhelpers/core/hugepage.py | |||
647 | index a783ad9..54b5b5e 100644 | |||
648 | --- a/hooks/charmhelpers/core/hugepage.py | |||
649 | +++ b/hooks/charmhelpers/core/hugepage.py | |||
650 | @@ -2,19 +2,17 @@ | |||
651 | 2 | 2 | ||
652 | 3 | # Copyright 2014-2015 Canonical Limited. | 3 | # Copyright 2014-2015 Canonical Limited. |
653 | 4 | # | 4 | # |
655 | 5 | # This file is part of charm-helpers. | 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
656 | 6 | # you may not use this file except in compliance with the License. | ||
657 | 7 | # You may obtain a copy of the License at | ||
658 | 6 | # | 8 | # |
662 | 7 | # charm-helpers is free software: you can redistribute it and/or modify | 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
660 | 8 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
661 | 9 | # published by the Free Software Foundation. | ||
663 | 10 | # | 10 | # |
671 | 11 | # charm-helpers is distributed in the hope that it will be useful, | 11 | # Unless required by applicable law or agreed to in writing, software |
672 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
673 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
674 | 14 | # GNU Lesser General Public License for more details. | 14 | # See the License for the specific language governing permissions and |
675 | 15 | # | 15 | # limitations under the License. |
669 | 16 | # You should have received a copy of the GNU Lesser General Public License | ||
670 | 17 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
676 | 18 | 16 | ||
677 | 19 | import yaml | 17 | import yaml |
678 | 20 | from charmhelpers.core import fstab | 18 | from charmhelpers.core import fstab |
679 | diff --git a/hooks/charmhelpers/core/kernel.py b/hooks/charmhelpers/core/kernel.py | |||
680 | index 5dc6495..2d40452 100644 | |||
681 | --- a/hooks/charmhelpers/core/kernel.py | |||
682 | +++ b/hooks/charmhelpers/core/kernel.py | |||
683 | @@ -3,29 +3,40 @@ | |||
684 | 3 | 3 | ||
685 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
686 | 5 | # | 5 | # |
688 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
689 | 7 | # you may not use this file except in compliance with the License. | ||
690 | 8 | # You may obtain a copy of the License at | ||
691 | 7 | # | 9 | # |
695 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
693 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
694 | 10 | # published by the Free Software Foundation. | ||
696 | 11 | # | 11 | # |
704 | 12 | # charm-helpers is distributed in the hope that it will be useful, | 12 | # Unless required by applicable law or agreed to in writing, software |
705 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
706 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
707 | 15 | # GNU Lesser General Public License for more details. | 15 | # See the License for the specific language governing permissions and |
708 | 16 | # | 16 | # limitations under the License. |
702 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
703 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
709 | 19 | 17 | ||
711 | 20 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | 18 | import re |
712 | 19 | import subprocess | ||
713 | 21 | 20 | ||
714 | 21 | from charmhelpers.osplatform import get_platform | ||
715 | 22 | from charmhelpers.core.hookenv import ( | 22 | from charmhelpers.core.hookenv import ( |
716 | 23 | log, | 23 | log, |
717 | 24 | INFO | 24 | INFO |
718 | 25 | ) | 25 | ) |
719 | 26 | 26 | ||
722 | 27 | from subprocess import check_call, check_output | 27 | __platform__ = get_platform() |
723 | 28 | import re | 28 | if __platform__ == "ubuntu": |
724 | 29 | from charmhelpers.core.kernel_factory.ubuntu import ( | ||
725 | 30 | persistent_modprobe, | ||
726 | 31 | update_initramfs, | ||
727 | 32 | ) # flake8: noqa -- ignore F401 for this import | ||
728 | 33 | elif __platform__ == "centos": | ||
729 | 34 | from charmhelpers.core.kernel_factory.centos import ( | ||
730 | 35 | persistent_modprobe, | ||
731 | 36 | update_initramfs, | ||
732 | 37 | ) # flake8: noqa -- ignore F401 for this import | ||
733 | 38 | |||
734 | 39 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
735 | 29 | 40 | ||
736 | 30 | 41 | ||
737 | 31 | def modprobe(module, persist=True): | 42 | def modprobe(module, persist=True): |
738 | @@ -34,11 +45,9 @@ def modprobe(module, persist=True): | |||
739 | 34 | 45 | ||
740 | 35 | log('Loading kernel module %s' % module, level=INFO) | 46 | log('Loading kernel module %s' % module, level=INFO) |
741 | 36 | 47 | ||
743 | 37 | check_call(cmd) | 48 | subprocess.check_call(cmd) |
744 | 38 | if persist: | 49 | if persist: |
748 | 39 | with open('/etc/modules', 'r+') as modules: | 50 | persistent_modprobe(module) |
746 | 40 | if module not in modules.read(): | ||
747 | 41 | modules.write(module) | ||
749 | 42 | 51 | ||
750 | 43 | 52 | ||
751 | 44 | def rmmod(module, force=False): | 53 | def rmmod(module, force=False): |
752 | @@ -48,21 +57,16 @@ def rmmod(module, force=False): | |||
753 | 48 | cmd.append('-f') | 57 | cmd.append('-f') |
754 | 49 | cmd.append(module) | 58 | cmd.append(module) |
755 | 50 | log('Removing kernel module %s' % module, level=INFO) | 59 | log('Removing kernel module %s' % module, level=INFO) |
757 | 51 | return check_call(cmd) | 60 | return subprocess.check_call(cmd) |
758 | 52 | 61 | ||
759 | 53 | 62 | ||
760 | 54 | def lsmod(): | 63 | def lsmod(): |
761 | 55 | """Shows what kernel modules are currently loaded""" | 64 | """Shows what kernel modules are currently loaded""" |
764 | 56 | return check_output(['lsmod'], | 65 | return subprocess.check_output(['lsmod'], |
765 | 57 | universal_newlines=True) | 66 | universal_newlines=True) |
766 | 58 | 67 | ||
767 | 59 | 68 | ||
768 | 60 | def is_module_loaded(module): | 69 | def is_module_loaded(module): |
769 | 61 | """Checks if a kernel module is already loaded""" | 70 | """Checks if a kernel module is already loaded""" |
770 | 62 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) | 71 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) |
771 | 63 | return len(matches) > 0 | 72 | return len(matches) > 0 |
772 | 64 | |||
773 | 65 | |||
774 | 66 | def update_initramfs(version='all'): | ||
775 | 67 | """Updates an initramfs image""" | ||
776 | 68 | return check_call(["update-initramfs", "-k", version, "-u"]) | ||
777 | diff --git a/hooks/charmhelpers/core/kernel_factory/__init__.py b/hooks/charmhelpers/core/kernel_factory/__init__.py | |||
778 | 69 | new file mode 100644 | 73 | new file mode 100644 |
779 | index 0000000..e69de29 | |||
780 | --- /dev/null | |||
781 | +++ b/hooks/charmhelpers/core/kernel_factory/__init__.py | |||
782 | diff --git a/hooks/charmhelpers/core/kernel_factory/centos.py b/hooks/charmhelpers/core/kernel_factory/centos.py | |||
783 | 70 | new file mode 100644 | 74 | new file mode 100644 |
784 | index 0000000..1c402c1 | |||
785 | --- /dev/null | |||
786 | +++ b/hooks/charmhelpers/core/kernel_factory/centos.py | |||
787 | @@ -0,0 +1,17 @@ | |||
788 | 1 | import subprocess | ||
789 | 2 | import os | ||
790 | 3 | |||
791 | 4 | |||
792 | 5 | def persistent_modprobe(module): | ||
793 | 6 | """Load a kernel module and configure for auto-load on reboot.""" | ||
794 | 7 | if not os.path.exists('/etc/rc.modules'): | ||
795 | 8 | open('/etc/rc.modules', 'a') | ||
796 | 9 | os.chmod('/etc/rc.modules', 111) | ||
797 | 10 | with open('/etc/rc.modules', 'r+') as modules: | ||
798 | 11 | if module not in modules.read(): | ||
799 | 12 | modules.write('modprobe %s\n' % module) | ||
800 | 13 | |||
801 | 14 | |||
802 | 15 | def update_initramfs(version='all'): | ||
803 | 16 | """Updates an initramfs image.""" | ||
804 | 17 | return subprocess.check_call(["dracut", "-f", version]) | ||
805 | diff --git a/hooks/charmhelpers/core/kernel_factory/ubuntu.py b/hooks/charmhelpers/core/kernel_factory/ubuntu.py | |||
806 | 0 | new file mode 100644 | 18 | new file mode 100644 |
807 | index 0000000..2155964 | |||
808 | --- /dev/null | |||
809 | +++ b/hooks/charmhelpers/core/kernel_factory/ubuntu.py | |||
810 | @@ -0,0 +1,13 @@ | |||
811 | 1 | import subprocess | ||
812 | 2 | |||
813 | 3 | |||
814 | 4 | def persistent_modprobe(module): | ||
815 | 5 | """Load a kernel module and configure for auto-load on reboot.""" | ||
816 | 6 | with open('/etc/modules', 'r+') as modules: | ||
817 | 7 | if module not in modules.read(): | ||
818 | 8 | modules.write(module) | ||
819 | 9 | |||
820 | 10 | |||
821 | 11 | def update_initramfs(version='all'): | ||
822 | 12 | """Updates an initramfs image.""" | ||
823 | 13 | return subprocess.check_call(["update-initramfs", "-k", version, "-u"]) | ||
824 | diff --git a/hooks/charmhelpers/core/services/__init__.py b/hooks/charmhelpers/core/services/__init__.py | |||
825 | index 0928158..61fd074 100644 | |||
826 | --- a/hooks/charmhelpers/core/services/__init__.py | |||
827 | +++ b/hooks/charmhelpers/core/services/__init__.py | |||
828 | @@ -1,18 +1,16 @@ | |||
829 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
830 | 2 | # | 2 | # |
832 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
833 | 4 | # you may not use this file except in compliance with the License. | ||
834 | 5 | # You may obtain a copy of the License at | ||
835 | 4 | # | 6 | # |
839 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
837 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
838 | 7 | # published by the Free Software Foundation. | ||
840 | 8 | # | 8 | # |
848 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
849 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
850 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
851 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
852 | 13 | # | 13 | # limitations under the License. |
846 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
847 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
853 | 16 | 14 | ||
854 | 17 | from .base import * # NOQA | 15 | from .base import * # NOQA |
855 | 18 | from .helpers import * # NOQA | 16 | from .helpers import * # NOQA |
856 | diff --git a/hooks/charmhelpers/core/services/base.py b/hooks/charmhelpers/core/services/base.py | |||
857 | index a42660c..ca9dc99 100644 | |||
858 | --- a/hooks/charmhelpers/core/services/base.py | |||
859 | +++ b/hooks/charmhelpers/core/services/base.py | |||
860 | @@ -1,18 +1,16 @@ | |||
861 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
862 | 2 | # | 2 | # |
864 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
865 | 4 | # you may not use this file except in compliance with the License. | ||
866 | 5 | # You may obtain a copy of the License at | ||
867 | 4 | # | 6 | # |
871 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
869 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
870 | 7 | # published by the Free Software Foundation. | ||
872 | 8 | # | 8 | # |
880 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
881 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
882 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
883 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
884 | 13 | # | 13 | # limitations under the License. |
878 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
879 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
885 | 16 | 14 | ||
886 | 17 | import os | 15 | import os |
887 | 18 | import json | 16 | import json |
888 | diff --git a/hooks/charmhelpers/core/services/helpers.py b/hooks/charmhelpers/core/services/helpers.py | |||
889 | index 2423704..3e6e30d 100644 | |||
890 | --- a/hooks/charmhelpers/core/services/helpers.py | |||
891 | +++ b/hooks/charmhelpers/core/services/helpers.py | |||
892 | @@ -1,18 +1,16 @@ | |||
893 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
894 | 2 | # | 2 | # |
896 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
897 | 4 | # you may not use this file except in compliance with the License. | ||
898 | 5 | # You may obtain a copy of the License at | ||
899 | 4 | # | 6 | # |
903 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
901 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
902 | 7 | # published by the Free Software Foundation. | ||
904 | 8 | # | 8 | # |
912 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
913 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
914 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
915 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
916 | 13 | # | 13 | # limitations under the License. |
910 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
911 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
917 | 16 | 14 | ||
918 | 17 | import os | 15 | import os |
919 | 18 | import yaml | 16 | import yaml |
920 | diff --git a/hooks/charmhelpers/core/strutils.py b/hooks/charmhelpers/core/strutils.py | |||
921 | index 7e3f969..dd9b971 100644 | |||
922 | --- a/hooks/charmhelpers/core/strutils.py | |||
923 | +++ b/hooks/charmhelpers/core/strutils.py | |||
924 | @@ -3,19 +3,17 @@ | |||
925 | 3 | 3 | ||
926 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
927 | 5 | # | 5 | # |
929 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
930 | 7 | # you may not use this file except in compliance with the License. | ||
931 | 8 | # You may obtain a copy of the License at | ||
932 | 7 | # | 9 | # |
936 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
934 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
935 | 10 | # published by the Free Software Foundation. | ||
937 | 11 | # | 11 | # |
945 | 12 | # charm-helpers is distributed in the hope that it will be useful, | 12 | # Unless required by applicable law or agreed to in writing, software |
946 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
947 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
948 | 15 | # GNU Lesser General Public License for more details. | 15 | # See the License for the specific language governing permissions and |
949 | 16 | # | 16 | # limitations under the License. |
943 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
944 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
950 | 19 | 17 | ||
951 | 20 | import six | 18 | import six |
952 | 21 | import re | 19 | import re |
953 | diff --git a/hooks/charmhelpers/core/sysctl.py b/hooks/charmhelpers/core/sysctl.py | |||
954 | index 21cc8ab..6e413e3 100644 | |||
955 | --- a/hooks/charmhelpers/core/sysctl.py | |||
956 | +++ b/hooks/charmhelpers/core/sysctl.py | |||
957 | @@ -3,19 +3,17 @@ | |||
958 | 3 | 3 | ||
959 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
960 | 5 | # | 5 | # |
962 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
963 | 7 | # you may not use this file except in compliance with the License. | ||
964 | 8 | # You may obtain a copy of the License at | ||
965 | 7 | # | 9 | # |
969 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
967 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
968 | 10 | # published by the Free Software Foundation. | ||
970 | 11 | # | 11 | # |
978 | 12 | # charm-helpers is distributed in the hope that it will be useful, | 12 | # Unless required by applicable law or agreed to in writing, software |
979 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # distributed under the License is distributed on an "AS IS" BASIS, |
980 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
981 | 15 | # GNU Lesser General Public License for more details. | 15 | # See the License for the specific language governing permissions and |
982 | 16 | # | 16 | # limitations under the License. |
976 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
977 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
983 | 19 | 17 | ||
984 | 20 | import yaml | 18 | import yaml |
985 | 21 | 19 | ||
986 | diff --git a/hooks/charmhelpers/core/templating.py b/hooks/charmhelpers/core/templating.py | |||
987 | index d2d8eaf..7b801a3 100644 | |||
988 | --- a/hooks/charmhelpers/core/templating.py | |||
989 | +++ b/hooks/charmhelpers/core/templating.py | |||
990 | @@ -1,20 +1,19 @@ | |||
991 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
992 | 2 | # | 2 | # |
994 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
995 | 4 | # you may not use this file except in compliance with the License. | ||
996 | 5 | # You may obtain a copy of the License at | ||
997 | 4 | # | 6 | # |
1001 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
999 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1000 | 7 | # published by the Free Software Foundation. | ||
1002 | 8 | # | 8 | # |
1010 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
1011 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
1012 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1013 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
1014 | 13 | # | 13 | # limitations under the License. |
1008 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
1009 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1015 | 16 | 14 | ||
1016 | 17 | import os | 15 | import os |
1017 | 16 | import sys | ||
1018 | 18 | 17 | ||
1019 | 19 | from charmhelpers.core import host | 18 | from charmhelpers.core import host |
1020 | 20 | from charmhelpers.core import hookenv | 19 | from charmhelpers.core import hookenv |
1021 | @@ -40,8 +39,9 @@ def render(source, target, context, owner='root', group='root', | |||
1022 | 40 | The rendered template will be written to the file as well as being returned | 39 | The rendered template will be written to the file as well as being returned |
1023 | 41 | as a string. | 40 | as a string. |
1024 | 42 | 41 | ||
1027 | 43 | Note: Using this requires python-jinja2; if it is not installed, calling | 42 | Note: Using this requires python-jinja2 or python3-jinja2; if it is not |
1028 | 44 | this will attempt to use charmhelpers.fetch.apt_install to install it. | 43 | installed, calling this will attempt to use charmhelpers.fetch.apt_install |
1029 | 44 | to install it. | ||
1030 | 45 | """ | 45 | """ |
1031 | 46 | try: | 46 | try: |
1032 | 47 | from jinja2 import FileSystemLoader, Environment, exceptions | 47 | from jinja2 import FileSystemLoader, Environment, exceptions |
1033 | @@ -53,7 +53,10 @@ def render(source, target, context, owner='root', group='root', | |||
1034 | 53 | 'charmhelpers.fetch to install it', | 53 | 'charmhelpers.fetch to install it', |
1035 | 54 | level=hookenv.ERROR) | 54 | level=hookenv.ERROR) |
1036 | 55 | raise | 55 | raise |
1038 | 56 | apt_install('python-jinja2', fatal=True) | 56 | if sys.version_info.major == 2: |
1039 | 57 | apt_install('python-jinja2', fatal=True) | ||
1040 | 58 | else: | ||
1041 | 59 | apt_install('python3-jinja2', fatal=True) | ||
1042 | 57 | from jinja2 import FileSystemLoader, Environment, exceptions | 60 | from jinja2 import FileSystemLoader, Environment, exceptions |
1043 | 58 | 61 | ||
1044 | 59 | if template_loader: | 62 | if template_loader: |
1045 | diff --git a/hooks/charmhelpers/core/unitdata.py b/hooks/charmhelpers/core/unitdata.py | |||
1046 | index 338104e..54ec969 100644 | |||
1047 | --- a/hooks/charmhelpers/core/unitdata.py | |||
1048 | +++ b/hooks/charmhelpers/core/unitdata.py | |||
1049 | @@ -3,20 +3,17 @@ | |||
1050 | 3 | # | 3 | # |
1051 | 4 | # Copyright 2014-2015 Canonical Limited. | 4 | # Copyright 2014-2015 Canonical Limited. |
1052 | 5 | # | 5 | # |
1054 | 6 | # This file is part of charm-helpers. | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); |
1055 | 7 | # you may not use this file except in compliance with the License. | ||
1056 | 8 | # You may obtain a copy of the License at | ||
1057 | 7 | # | 9 | # |
1069 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
1059 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1060 | 10 | # published by the Free Software Foundation. | ||
1061 | 11 | # | ||
1062 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
1063 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1064 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1065 | 15 | # GNU Lesser General Public License for more details. | ||
1066 | 16 | # | ||
1067 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
1068 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1070 | 19 | # | 11 | # |
1071 | 12 | # Unless required by applicable law or agreed to in writing, software | ||
1072 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
1073 | 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
1074 | 15 | # See the License for the specific language governing permissions and | ||
1075 | 16 | # limitations under the License. | ||
1076 | 20 | # | 17 | # |
1077 | 21 | # Authors: | 18 | # Authors: |
1078 | 22 | # Kapil Thangavelu <kapil.foss@gmail.com> | 19 | # Kapil Thangavelu <kapil.foss@gmail.com> |
1079 | diff --git a/hooks/charmhelpers/fetch/__init__.py b/hooks/charmhelpers/fetch/__init__.py | |||
1080 | index db0d86a..ec5e0fe 100644 | |||
1081 | --- a/hooks/charmhelpers/fetch/__init__.py | |||
1082 | +++ b/hooks/charmhelpers/fetch/__init__.py | |||
1083 | @@ -1,32 +1,24 @@ | |||
1084 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
1085 | 2 | # | 2 | # |
1087 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
1088 | 4 | # you may not use this file except in compliance with the License. | ||
1089 | 5 | # You may obtain a copy of the License at | ||
1090 | 4 | # | 6 | # |
1094 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
1092 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1093 | 7 | # published by the Free Software Foundation. | ||
1095 | 8 | # | 8 | # |
1103 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
1104 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
1105 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1106 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
1107 | 13 | # | 13 | # limitations under the License. |
1101 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
1102 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1108 | 16 | 14 | ||
1109 | 17 | import importlib | 15 | import importlib |
1112 | 18 | from tempfile import NamedTemporaryFile | 16 | from charmhelpers.osplatform import get_platform |
1111 | 19 | import time | ||
1113 | 20 | from yaml import safe_load | 17 | from yaml import safe_load |
1114 | 21 | from charmhelpers.core.host import ( | ||
1115 | 22 | lsb_release | ||
1116 | 23 | ) | ||
1117 | 24 | import subprocess | ||
1118 | 25 | from charmhelpers.core.hookenv import ( | 18 | from charmhelpers.core.hookenv import ( |
1119 | 26 | config, | 19 | config, |
1120 | 27 | log, | 20 | log, |
1121 | 28 | ) | 21 | ) |
1122 | 29 | import os | ||
1123 | 30 | 22 | ||
1124 | 31 | import six | 23 | import six |
1125 | 32 | if six.PY3: | 24 | if six.PY3: |
1126 | @@ -35,79 +27,6 @@ else: | |||
1127 | 35 | from urlparse import urlparse, urlunparse | 27 | from urlparse import urlparse, urlunparse |
1128 | 36 | 28 | ||
1129 | 37 | 29 | ||
1130 | 38 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive | ||
1131 | 39 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main | ||
1132 | 40 | """ | ||
1133 | 41 | PROPOSED_POCKET = """# Proposed | ||
1134 | 42 | deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted | ||
1135 | 43 | """ | ||
1136 | 44 | CLOUD_ARCHIVE_POCKETS = { | ||
1137 | 45 | # Folsom | ||
1138 | 46 | 'folsom': 'precise-updates/folsom', | ||
1139 | 47 | 'precise-folsom': 'precise-updates/folsom', | ||
1140 | 48 | 'precise-folsom/updates': 'precise-updates/folsom', | ||
1141 | 49 | 'precise-updates/folsom': 'precise-updates/folsom', | ||
1142 | 50 | 'folsom/proposed': 'precise-proposed/folsom', | ||
1143 | 51 | 'precise-folsom/proposed': 'precise-proposed/folsom', | ||
1144 | 52 | 'precise-proposed/folsom': 'precise-proposed/folsom', | ||
1145 | 53 | # Grizzly | ||
1146 | 54 | 'grizzly': 'precise-updates/grizzly', | ||
1147 | 55 | 'precise-grizzly': 'precise-updates/grizzly', | ||
1148 | 56 | 'precise-grizzly/updates': 'precise-updates/grizzly', | ||
1149 | 57 | 'precise-updates/grizzly': 'precise-updates/grizzly', | ||
1150 | 58 | 'grizzly/proposed': 'precise-proposed/grizzly', | ||
1151 | 59 | 'precise-grizzly/proposed': 'precise-proposed/grizzly', | ||
1152 | 60 | 'precise-proposed/grizzly': 'precise-proposed/grizzly', | ||
1153 | 61 | # Havana | ||
1154 | 62 | 'havana': 'precise-updates/havana', | ||
1155 | 63 | 'precise-havana': 'precise-updates/havana', | ||
1156 | 64 | 'precise-havana/updates': 'precise-updates/havana', | ||
1157 | 65 | 'precise-updates/havana': 'precise-updates/havana', | ||
1158 | 66 | 'havana/proposed': 'precise-proposed/havana', | ||
1159 | 67 | 'precise-havana/proposed': 'precise-proposed/havana', | ||
1160 | 68 | 'precise-proposed/havana': 'precise-proposed/havana', | ||
1161 | 69 | # Icehouse | ||
1162 | 70 | 'icehouse': 'precise-updates/icehouse', | ||
1163 | 71 | 'precise-icehouse': 'precise-updates/icehouse', | ||
1164 | 72 | 'precise-icehouse/updates': 'precise-updates/icehouse', | ||
1165 | 73 | 'precise-updates/icehouse': 'precise-updates/icehouse', | ||
1166 | 74 | 'icehouse/proposed': 'precise-proposed/icehouse', | ||
1167 | 75 | 'precise-icehouse/proposed': 'precise-proposed/icehouse', | ||
1168 | 76 | 'precise-proposed/icehouse': 'precise-proposed/icehouse', | ||
1169 | 77 | # Juno | ||
1170 | 78 | 'juno': 'trusty-updates/juno', | ||
1171 | 79 | 'trusty-juno': 'trusty-updates/juno', | ||
1172 | 80 | 'trusty-juno/updates': 'trusty-updates/juno', | ||
1173 | 81 | 'trusty-updates/juno': 'trusty-updates/juno', | ||
1174 | 82 | 'juno/proposed': 'trusty-proposed/juno', | ||
1175 | 83 | 'trusty-juno/proposed': 'trusty-proposed/juno', | ||
1176 | 84 | 'trusty-proposed/juno': 'trusty-proposed/juno', | ||
1177 | 85 | # Kilo | ||
1178 | 86 | 'kilo': 'trusty-updates/kilo', | ||
1179 | 87 | 'trusty-kilo': 'trusty-updates/kilo', | ||
1180 | 88 | 'trusty-kilo/updates': 'trusty-updates/kilo', | ||
1181 | 89 | 'trusty-updates/kilo': 'trusty-updates/kilo', | ||
1182 | 90 | 'kilo/proposed': 'trusty-proposed/kilo', | ||
1183 | 91 | 'trusty-kilo/proposed': 'trusty-proposed/kilo', | ||
1184 | 92 | 'trusty-proposed/kilo': 'trusty-proposed/kilo', | ||
1185 | 93 | # Liberty | ||
1186 | 94 | 'liberty': 'trusty-updates/liberty', | ||
1187 | 95 | 'trusty-liberty': 'trusty-updates/liberty', | ||
1188 | 96 | 'trusty-liberty/updates': 'trusty-updates/liberty', | ||
1189 | 97 | 'trusty-updates/liberty': 'trusty-updates/liberty', | ||
1190 | 98 | 'liberty/proposed': 'trusty-proposed/liberty', | ||
1191 | 99 | 'trusty-liberty/proposed': 'trusty-proposed/liberty', | ||
1192 | 100 | 'trusty-proposed/liberty': 'trusty-proposed/liberty', | ||
1193 | 101 | # Mitaka | ||
1194 | 102 | 'mitaka': 'trusty-updates/mitaka', | ||
1195 | 103 | 'trusty-mitaka': 'trusty-updates/mitaka', | ||
1196 | 104 | 'trusty-mitaka/updates': 'trusty-updates/mitaka', | ||
1197 | 105 | 'trusty-updates/mitaka': 'trusty-updates/mitaka', | ||
1198 | 106 | 'mitaka/proposed': 'trusty-proposed/mitaka', | ||
1199 | 107 | 'trusty-mitaka/proposed': 'trusty-proposed/mitaka', | ||
1200 | 108 | 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', | ||
1201 | 109 | } | ||
1202 | 110 | |||
1203 | 111 | # The order of this list is very important. Handlers should be listed in from | 30 | # The order of this list is very important. Handlers should be listed in from |
1204 | 112 | # least- to most-specific URL matching. | 31 | # least- to most-specific URL matching. |
1205 | 113 | FETCH_HANDLERS = ( | 32 | FETCH_HANDLERS = ( |
1206 | @@ -116,10 +35,6 @@ FETCH_HANDLERS = ( | |||
1207 | 116 | 'charmhelpers.fetch.giturl.GitUrlFetchHandler', | 35 | 'charmhelpers.fetch.giturl.GitUrlFetchHandler', |
1208 | 117 | ) | 36 | ) |
1209 | 118 | 37 | ||
1210 | 119 | APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. | ||
1211 | 120 | APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. | ||
1212 | 121 | APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. | ||
1213 | 122 | |||
1214 | 123 | 38 | ||
1215 | 124 | class SourceConfigError(Exception): | 39 | class SourceConfigError(Exception): |
1216 | 125 | pass | 40 | pass |
1217 | @@ -157,180 +72,38 @@ class BaseFetchHandler(object): | |||
1218 | 157 | return urlunparse(parts) | 72 | return urlunparse(parts) |
1219 | 158 | 73 | ||
1220 | 159 | 74 | ||
1308 | 160 | def filter_installed_packages(packages): | 75 | __platform__ = get_platform() |
1309 | 161 | """Returns a list of packages that require installation""" | 76 | module = "charmhelpers.fetch.%s" % __platform__ |
1310 | 162 | cache = apt_cache() | 77 | fetch = importlib.import_module(module) |
1224 | 163 | _pkgs = [] | ||
1225 | 164 | for package in packages: | ||
1226 | 165 | try: | ||
1227 | 166 | p = cache[package] | ||
1228 | 167 | p.current_ver or _pkgs.append(package) | ||
1229 | 168 | except KeyError: | ||
1230 | 169 | log('Package {} has no installation candidate.'.format(package), | ||
1231 | 170 | level='WARNING') | ||
1232 | 171 | _pkgs.append(package) | ||
1233 | 172 | return _pkgs | ||
1234 | 173 | |||
1235 | 174 | |||
1236 | 175 | def apt_cache(in_memory=True): | ||
1237 | 176 | """Build and return an apt cache""" | ||
1238 | 177 | from apt import apt_pkg | ||
1239 | 178 | apt_pkg.init() | ||
1240 | 179 | if in_memory: | ||
1241 | 180 | apt_pkg.config.set("Dir::Cache::pkgcache", "") | ||
1242 | 181 | apt_pkg.config.set("Dir::Cache::srcpkgcache", "") | ||
1243 | 182 | return apt_pkg.Cache() | ||
1244 | 183 | |||
1245 | 184 | |||
1246 | 185 | def apt_install(packages, options=None, fatal=False): | ||
1247 | 186 | """Install one or more packages""" | ||
1248 | 187 | if options is None: | ||
1249 | 188 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
1250 | 189 | |||
1251 | 190 | cmd = ['apt-get', '--assume-yes'] | ||
1252 | 191 | cmd.extend(options) | ||
1253 | 192 | cmd.append('install') | ||
1254 | 193 | if isinstance(packages, six.string_types): | ||
1255 | 194 | cmd.append(packages) | ||
1256 | 195 | else: | ||
1257 | 196 | cmd.extend(packages) | ||
1258 | 197 | log("Installing {} with options: {}".format(packages, | ||
1259 | 198 | options)) | ||
1260 | 199 | _run_apt_command(cmd, fatal) | ||
1261 | 200 | |||
1262 | 201 | |||
1263 | 202 | def apt_upgrade(options=None, fatal=False, dist=False): | ||
1264 | 203 | """Upgrade all packages""" | ||
1265 | 204 | if options is None: | ||
1266 | 205 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
1267 | 206 | |||
1268 | 207 | cmd = ['apt-get', '--assume-yes'] | ||
1269 | 208 | cmd.extend(options) | ||
1270 | 209 | if dist: | ||
1271 | 210 | cmd.append('dist-upgrade') | ||
1272 | 211 | else: | ||
1273 | 212 | cmd.append('upgrade') | ||
1274 | 213 | log("Upgrading with options: {}".format(options)) | ||
1275 | 214 | _run_apt_command(cmd, fatal) | ||
1276 | 215 | |||
1277 | 216 | |||
1278 | 217 | def apt_update(fatal=False): | ||
1279 | 218 | """Update local apt cache""" | ||
1280 | 219 | cmd = ['apt-get', 'update'] | ||
1281 | 220 | _run_apt_command(cmd, fatal) | ||
1282 | 221 | |||
1283 | 222 | |||
1284 | 223 | def apt_purge(packages, fatal=False): | ||
1285 | 224 | """Purge one or more packages""" | ||
1286 | 225 | cmd = ['apt-get', '--assume-yes', 'purge'] | ||
1287 | 226 | if isinstance(packages, six.string_types): | ||
1288 | 227 | cmd.append(packages) | ||
1289 | 228 | else: | ||
1290 | 229 | cmd.extend(packages) | ||
1291 | 230 | log("Purging {}".format(packages)) | ||
1292 | 231 | _run_apt_command(cmd, fatal) | ||
1293 | 232 | |||
1294 | 233 | |||
1295 | 234 | def apt_mark(packages, mark, fatal=False): | ||
1296 | 235 | """Flag one or more packages using apt-mark""" | ||
1297 | 236 | log("Marking {} as {}".format(packages, mark)) | ||
1298 | 237 | cmd = ['apt-mark', mark] | ||
1299 | 238 | if isinstance(packages, six.string_types): | ||
1300 | 239 | cmd.append(packages) | ||
1301 | 240 | else: | ||
1302 | 241 | cmd.extend(packages) | ||
1303 | 242 | |||
1304 | 243 | if fatal: | ||
1305 | 244 | subprocess.check_call(cmd, universal_newlines=True) | ||
1306 | 245 | else: | ||
1307 | 246 | subprocess.call(cmd, universal_newlines=True) | ||
1311 | 247 | 78 | ||
1312 | 79 | filter_installed_packages = fetch.filter_installed_packages | ||
1313 | 80 | install = fetch.install | ||
1314 | 81 | upgrade = fetch.upgrade | ||
1315 | 82 | update = fetch.update | ||
1316 | 83 | purge = fetch.purge | ||
1317 | 84 | add_source = fetch.add_source | ||
1318 | 248 | 85 | ||
1394 | 249 | def apt_hold(packages, fatal=False): | 86 | if __platform__ == "ubuntu": |
1395 | 250 | return apt_mark(packages, 'hold', fatal=fatal) | 87 | apt_cache = fetch.apt_cache |
1396 | 251 | 88 | apt_install = fetch.install | |
1397 | 252 | 89 | apt_update = fetch.update | |
1398 | 253 | def apt_unhold(packages, fatal=False): | 90 | apt_upgrade = fetch.upgrade |
1399 | 254 | return apt_mark(packages, 'unhold', fatal=fatal) | 91 | apt_purge = fetch.purge |
1400 | 255 | 92 | apt_mark = fetch.apt_mark | |
1401 | 256 | 93 | apt_hold = fetch.apt_hold | |
1402 | 257 | def add_source(source, key=None): | 94 | apt_unhold = fetch.apt_unhold |
1403 | 258 | """Add a package source to this system. | 95 | get_upstream_version = fetch.get_upstream_version |
1404 | 259 | 96 | elif __platform__ == "centos": | |
1405 | 260 | @param source: a URL or sources.list entry, as supported by | 97 | yum_search = fetch.yum_search |
1331 | 261 | add-apt-repository(1). Examples:: | ||
1332 | 262 | |||
1333 | 263 | ppa:charmers/example | ||
1334 | 264 | deb https://stub:key@private.example.com/ubuntu trusty main | ||
1335 | 265 | |||
1336 | 266 | In addition: | ||
1337 | 267 | 'proposed:' may be used to enable the standard 'proposed' | ||
1338 | 268 | pocket for the release. | ||
1339 | 269 | 'cloud:' may be used to activate official cloud archive pockets, | ||
1340 | 270 | such as 'cloud:icehouse' | ||
1341 | 271 | 'distro' may be used as a noop | ||
1342 | 272 | |||
1343 | 273 | @param key: A key to be added to the system's APT keyring and used | ||
1344 | 274 | to verify the signatures on packages. Ideally, this should be an | ||
1345 | 275 | ASCII format GPG public key including the block headers. A GPG key | ||
1346 | 276 | id may also be used, but be aware that only insecure protocols are | ||
1347 | 277 | available to retrieve the actual public key from a public keyserver | ||
1348 | 278 | placing your Juju environment at risk. ppa and cloud archive keys | ||
1349 | 279 | are securely added automtically, so sould not be provided. | ||
1350 | 280 | """ | ||
1351 | 281 | if source is None: | ||
1352 | 282 | log('Source is not present. Skipping') | ||
1353 | 283 | return | ||
1354 | 284 | |||
1355 | 285 | if (source.startswith('ppa:') or | ||
1356 | 286 | source.startswith('http') or | ||
1357 | 287 | source.startswith('deb ') or | ||
1358 | 288 | source.startswith('cloud-archive:')): | ||
1359 | 289 | subprocess.check_call(['add-apt-repository', '--yes', source]) | ||
1360 | 290 | elif source.startswith('cloud:'): | ||
1361 | 291 | apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), | ||
1362 | 292 | fatal=True) | ||
1363 | 293 | pocket = source.split(':')[-1] | ||
1364 | 294 | if pocket not in CLOUD_ARCHIVE_POCKETS: | ||
1365 | 295 | raise SourceConfigError( | ||
1366 | 296 | 'Unsupported cloud: source option %s' % | ||
1367 | 297 | pocket) | ||
1368 | 298 | actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] | ||
1369 | 299 | with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: | ||
1370 | 300 | apt.write(CLOUD_ARCHIVE.format(actual_pocket)) | ||
1371 | 301 | elif source == 'proposed': | ||
1372 | 302 | release = lsb_release()['DISTRIB_CODENAME'] | ||
1373 | 303 | with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: | ||
1374 | 304 | apt.write(PROPOSED_POCKET.format(release)) | ||
1375 | 305 | elif source == 'distro': | ||
1376 | 306 | pass | ||
1377 | 307 | else: | ||
1378 | 308 | log("Unknown source: {!r}".format(source)) | ||
1379 | 309 | |||
1380 | 310 | if key: | ||
1381 | 311 | if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: | ||
1382 | 312 | with NamedTemporaryFile('w+') as key_file: | ||
1383 | 313 | key_file.write(key) | ||
1384 | 314 | key_file.flush() | ||
1385 | 315 | key_file.seek(0) | ||
1386 | 316 | subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file) | ||
1387 | 317 | else: | ||
1388 | 318 | # Note that hkp: is in no way a secure protocol. Using a | ||
1389 | 319 | # GPG key id is pointless from a security POV unless you | ||
1390 | 320 | # absolutely trust your network and DNS. | ||
1391 | 321 | subprocess.check_call(['apt-key', 'adv', '--keyserver', | ||
1392 | 322 | 'hkp://keyserver.ubuntu.com:80', '--recv', | ||
1393 | 323 | key]) | ||
1406 | 324 | 98 | ||
1407 | 325 | 99 | ||
1408 | 326 | def configure_sources(update=False, | 100 | def configure_sources(update=False, |
1409 | 327 | sources_var='install_sources', | 101 | sources_var='install_sources', |
1410 | 328 | keys_var='install_keys'): | 102 | keys_var='install_keys'): |
1413 | 329 | """ | 103 | """Configure multiple sources from charm configuration. |
1412 | 330 | Configure multiple sources from charm configuration. | ||
1414 | 331 | 104 | ||
1415 | 332 | The lists are encoded as yaml fragments in the configuration. | 105 | The lists are encoded as yaml fragments in the configuration. |
1417 | 333 | The frament needs to be included as a string. Sources and their | 106 | The fragment needs to be included as a string. Sources and their |
1418 | 334 | corresponding keys are of the types supported by add_source(). | 107 | corresponding keys are of the types supported by add_source(). |
1419 | 335 | 108 | ||
1420 | 336 | Example config: | 109 | Example config: |
1421 | @@ -362,12 +135,11 @@ def configure_sources(update=False, | |||
1422 | 362 | for source, key in zip(sources, keys): | 135 | for source, key in zip(sources, keys): |
1423 | 363 | add_source(source, key) | 136 | add_source(source, key) |
1424 | 364 | if update: | 137 | if update: |
1426 | 365 | apt_update(fatal=True) | 138 | fetch.update(fatal=True) |
1427 | 366 | 139 | ||
1428 | 367 | 140 | ||
1429 | 368 | def install_remote(source, *args, **kwargs): | 141 | def install_remote(source, *args, **kwargs): |
1432 | 369 | """ | 142 | """Install a file tree from a remote source. |
1431 | 370 | Install a file tree from a remote source | ||
1433 | 371 | 143 | ||
1434 | 372 | The specified source should be a url of the form: | 144 | The specified source should be a url of the form: |
1435 | 373 | scheme://[host]/path[#[option=value][&...]] | 145 | scheme://[host]/path[#[option=value][&...]] |
1436 | @@ -390,19 +162,17 @@ def install_remote(source, *args, **kwargs): | |||
1437 | 390 | # We ONLY check for True here because can_handle may return a string | 162 | # We ONLY check for True here because can_handle may return a string |
1438 | 391 | # explaining why it can't handle a given source. | 163 | # explaining why it can't handle a given source. |
1439 | 392 | handlers = [h for h in plugins() if h.can_handle(source) is True] | 164 | handlers = [h for h in plugins() if h.can_handle(source) is True] |
1440 | 393 | installed_to = None | ||
1441 | 394 | for handler in handlers: | 165 | for handler in handlers: |
1442 | 395 | try: | 166 | try: |
1444 | 396 | installed_to = handler.install(source, *args, **kwargs) | 167 | return handler.install(source, *args, **kwargs) |
1445 | 397 | except UnhandledSource as e: | 168 | except UnhandledSource as e: |
1446 | 398 | log('Install source attempt unsuccessful: {}'.format(e), | 169 | log('Install source attempt unsuccessful: {}'.format(e), |
1447 | 399 | level='WARNING') | 170 | level='WARNING') |
1451 | 400 | if not installed_to: | 171 | raise UnhandledSource("No handler found for source {}".format(source)) |
1449 | 401 | raise UnhandledSource("No handler found for source {}".format(source)) | ||
1450 | 402 | return installed_to | ||
1452 | 403 | 172 | ||
1453 | 404 | 173 | ||
1454 | 405 | def install_from_config(config_var_name): | 174 | def install_from_config(config_var_name): |
1455 | 175 | """Install a file from config.""" | ||
1456 | 406 | charm_config = config() | 176 | charm_config = config() |
1457 | 407 | source = charm_config[config_var_name] | 177 | source = charm_config[config_var_name] |
1458 | 408 | return install_remote(source) | 178 | return install_remote(source) |
1459 | @@ -425,40 +195,3 @@ def plugins(fetch_handlers=None): | |||
1460 | 425 | log("FetchHandler {} not found, skipping plugin".format( | 195 | log("FetchHandler {} not found, skipping plugin".format( |
1461 | 426 | handler_name)) | 196 | handler_name)) |
1462 | 427 | return plugin_list | 197 | return plugin_list |
1463 | 428 | |||
1464 | 429 | |||
1465 | 430 | def _run_apt_command(cmd, fatal=False): | ||
1466 | 431 | """ | ||
1467 | 432 | Run an APT command, checking output and retrying if the fatal flag is set | ||
1468 | 433 | to True. | ||
1469 | 434 | |||
1470 | 435 | :param: cmd: str: The apt command to run. | ||
1471 | 436 | :param: fatal: bool: Whether the command's output should be checked and | ||
1472 | 437 | retried. | ||
1473 | 438 | """ | ||
1474 | 439 | env = os.environ.copy() | ||
1475 | 440 | |||
1476 | 441 | if 'DEBIAN_FRONTEND' not in env: | ||
1477 | 442 | env['DEBIAN_FRONTEND'] = 'noninteractive' | ||
1478 | 443 | |||
1479 | 444 | if fatal: | ||
1480 | 445 | retry_count = 0 | ||
1481 | 446 | result = None | ||
1482 | 447 | |||
1483 | 448 | # If the command is considered "fatal", we need to retry if the apt | ||
1484 | 449 | # lock was not acquired. | ||
1485 | 450 | |||
1486 | 451 | while result is None or result == APT_NO_LOCK: | ||
1487 | 452 | try: | ||
1488 | 453 | result = subprocess.check_call(cmd, env=env) | ||
1489 | 454 | except subprocess.CalledProcessError as e: | ||
1490 | 455 | retry_count = retry_count + 1 | ||
1491 | 456 | if retry_count > APT_NO_LOCK_RETRY_COUNT: | ||
1492 | 457 | raise | ||
1493 | 458 | result = e.returncode | ||
1494 | 459 | log("Couldn't acquire DPKG lock. Will retry in {} seconds." | ||
1495 | 460 | "".format(APT_NO_LOCK_RETRY_DELAY)) | ||
1496 | 461 | time.sleep(APT_NO_LOCK_RETRY_DELAY) | ||
1497 | 462 | |||
1498 | 463 | else: | ||
1499 | 464 | subprocess.call(cmd, env=env) | ||
1500 | diff --git a/hooks/charmhelpers/fetch/archiveurl.py b/hooks/charmhelpers/fetch/archiveurl.py | |||
1501 | index b8e0943..dd24f9e 100644 | |||
1502 | --- a/hooks/charmhelpers/fetch/archiveurl.py | |||
1503 | +++ b/hooks/charmhelpers/fetch/archiveurl.py | |||
1504 | @@ -1,18 +1,16 @@ | |||
1505 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
1506 | 2 | # | 2 | # |
1508 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
1509 | 4 | # you may not use this file except in compliance with the License. | ||
1510 | 5 | # You may obtain a copy of the License at | ||
1511 | 4 | # | 6 | # |
1515 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
1513 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1514 | 7 | # published by the Free Software Foundation. | ||
1516 | 8 | # | 8 | # |
1524 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
1525 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
1526 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1527 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
1528 | 13 | # | 13 | # limitations under the License. |
1522 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
1523 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1529 | 16 | 14 | ||
1530 | 17 | import os | 15 | import os |
1531 | 18 | import hashlib | 16 | import hashlib |
1532 | diff --git a/hooks/charmhelpers/fetch/bzrurl.py b/hooks/charmhelpers/fetch/bzrurl.py | |||
1533 | index cafd27f..07cd029 100644 | |||
1534 | --- a/hooks/charmhelpers/fetch/bzrurl.py | |||
1535 | +++ b/hooks/charmhelpers/fetch/bzrurl.py | |||
1536 | @@ -1,18 +1,16 @@ | |||
1537 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
1538 | 2 | # | 2 | # |
1540 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
1541 | 4 | # you may not use this file except in compliance with the License. | ||
1542 | 5 | # You may obtain a copy of the License at | ||
1543 | 4 | # | 6 | # |
1547 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
1545 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1546 | 7 | # published by the Free Software Foundation. | ||
1548 | 8 | # | 8 | # |
1556 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
1557 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
1558 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1559 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
1560 | 13 | # | 13 | # limitations under the License. |
1554 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
1555 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1561 | 16 | 14 | ||
1562 | 17 | import os | 15 | import os |
1563 | 18 | from subprocess import check_call | 16 | from subprocess import check_call |
1564 | @@ -20,19 +18,20 @@ from charmhelpers.fetch import ( | |||
1565 | 20 | BaseFetchHandler, | 18 | BaseFetchHandler, |
1566 | 21 | UnhandledSource, | 19 | UnhandledSource, |
1567 | 22 | filter_installed_packages, | 20 | filter_installed_packages, |
1569 | 23 | apt_install, | 21 | install, |
1570 | 24 | ) | 22 | ) |
1571 | 25 | from charmhelpers.core.host import mkdir | 23 | from charmhelpers.core.host import mkdir |
1572 | 26 | 24 | ||
1573 | 27 | 25 | ||
1574 | 28 | if filter_installed_packages(['bzr']) != []: | 26 | if filter_installed_packages(['bzr']) != []: |
1576 | 29 | apt_install(['bzr']) | 27 | install(['bzr']) |
1577 | 30 | if filter_installed_packages(['bzr']) != []: | 28 | if filter_installed_packages(['bzr']) != []: |
1578 | 31 | raise NotImplementedError('Unable to install bzr') | 29 | raise NotImplementedError('Unable to install bzr') |
1579 | 32 | 30 | ||
1580 | 33 | 31 | ||
1581 | 34 | class BzrUrlFetchHandler(BaseFetchHandler): | 32 | class BzrUrlFetchHandler(BaseFetchHandler): |
1583 | 35 | """Handler for bazaar branches via generic and lp URLs""" | 33 | """Handler for bazaar branches via generic and lp URLs.""" |
1584 | 34 | |||
1585 | 36 | def can_handle(self, source): | 35 | def can_handle(self, source): |
1586 | 37 | url_parts = self.parse_url(source) | 36 | url_parts = self.parse_url(source) |
1587 | 38 | if url_parts.scheme not in ('bzr+ssh', 'lp', ''): | 37 | if url_parts.scheme not in ('bzr+ssh', 'lp', ''): |
1588 | @@ -42,15 +41,23 @@ class BzrUrlFetchHandler(BaseFetchHandler): | |||
1589 | 42 | else: | 41 | else: |
1590 | 43 | return True | 42 | return True |
1591 | 44 | 43 | ||
1593 | 45 | def branch(self, source, dest): | 44 | def branch(self, source, dest, revno=None): |
1594 | 46 | if not self.can_handle(source): | 45 | if not self.can_handle(source): |
1595 | 47 | raise UnhandledSource("Cannot handle {}".format(source)) | 46 | raise UnhandledSource("Cannot handle {}".format(source)) |
1596 | 47 | cmd_opts = [] | ||
1597 | 48 | if revno: | ||
1598 | 49 | cmd_opts += ['-r', str(revno)] | ||
1599 | 48 | if os.path.exists(dest): | 50 | if os.path.exists(dest): |
1601 | 49 | check_call(['bzr', 'pull', '--overwrite', '-d', dest, source]) | 51 | cmd = ['bzr', 'pull'] |
1602 | 52 | cmd += cmd_opts | ||
1603 | 53 | cmd += ['--overwrite', '-d', dest, source] | ||
1604 | 50 | else: | 54 | else: |
1606 | 51 | check_call(['bzr', 'branch', source, dest]) | 55 | cmd = ['bzr', 'branch'] |
1607 | 56 | cmd += cmd_opts | ||
1608 | 57 | cmd += [source, dest] | ||
1609 | 58 | check_call(cmd) | ||
1610 | 52 | 59 | ||
1612 | 53 | def install(self, source, dest=None): | 60 | def install(self, source, dest=None, revno=None): |
1613 | 54 | url_parts = self.parse_url(source) | 61 | url_parts = self.parse_url(source) |
1614 | 55 | branch_name = url_parts.path.strip("/").split("/")[-1] | 62 | branch_name = url_parts.path.strip("/").split("/")[-1] |
1615 | 56 | if dest: | 63 | if dest: |
1616 | @@ -59,10 +66,11 @@ class BzrUrlFetchHandler(BaseFetchHandler): | |||
1617 | 59 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", | 66 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", |
1618 | 60 | branch_name) | 67 | branch_name) |
1619 | 61 | 68 | ||
1622 | 62 | if not os.path.exists(dest_dir): | 69 | if dest and not os.path.exists(dest): |
1623 | 63 | mkdir(dest_dir, perms=0o755) | 70 | mkdir(dest, perms=0o755) |
1624 | 71 | |||
1625 | 64 | try: | 72 | try: |
1627 | 65 | self.branch(source, dest_dir) | 73 | self.branch(source, dest_dir, revno) |
1628 | 66 | except OSError as e: | 74 | except OSError as e: |
1629 | 67 | raise UnhandledSource(e.strerror) | 75 | raise UnhandledSource(e.strerror) |
1630 | 68 | return dest_dir | 76 | return dest_dir |
1631 | diff --git a/hooks/charmhelpers/fetch/centos.py b/hooks/charmhelpers/fetch/centos.py | |||
1632 | 69 | new file mode 100644 | 77 | new file mode 100644 |
1633 | index 0000000..604bbfb | |||
1634 | --- /dev/null | |||
1635 | +++ b/hooks/charmhelpers/fetch/centos.py | |||
1636 | @@ -0,0 +1,171 @@ | |||
1637 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
1638 | 2 | # | ||
1639 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
1640 | 4 | # you may not use this file except in compliance with the License. | ||
1641 | 5 | # You may obtain a copy of the License at | ||
1642 | 6 | # | ||
1643 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
1644 | 8 | # | ||
1645 | 9 | # Unless required by applicable law or agreed to in writing, software | ||
1646 | 10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
1647 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
1648 | 12 | # See the License for the specific language governing permissions and | ||
1649 | 13 | # limitations under the License. | ||
1650 | 14 | |||
1651 | 15 | import subprocess | ||
1652 | 16 | import os | ||
1653 | 17 | import time | ||
1654 | 18 | import six | ||
1655 | 19 | import yum | ||
1656 | 20 | |||
1657 | 21 | from tempfile import NamedTemporaryFile | ||
1658 | 22 | from charmhelpers.core.hookenv import log | ||
1659 | 23 | |||
1660 | 24 | YUM_NO_LOCK = 1 # The return code for "couldn't acquire lock" in YUM. | ||
1661 | 25 | YUM_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. | ||
1662 | 26 | YUM_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. | ||
1663 | 27 | |||
1664 | 28 | |||
1665 | 29 | def filter_installed_packages(packages): | ||
1666 | 30 | """Return a list of packages that require installation.""" | ||
1667 | 31 | yb = yum.YumBase() | ||
1668 | 32 | package_list = yb.doPackageLists() | ||
1669 | 33 | temp_cache = {p.base_package_name: 1 for p in package_list['installed']} | ||
1670 | 34 | |||
1671 | 35 | _pkgs = [p for p in packages if not temp_cache.get(p, False)] | ||
1672 | 36 | return _pkgs | ||
1673 | 37 | |||
1674 | 38 | |||
1675 | 39 | def install(packages, options=None, fatal=False): | ||
1676 | 40 | """Install one or more packages.""" | ||
1677 | 41 | cmd = ['yum', '--assumeyes'] | ||
1678 | 42 | if options is not None: | ||
1679 | 43 | cmd.extend(options) | ||
1680 | 44 | cmd.append('install') | ||
1681 | 45 | if isinstance(packages, six.string_types): | ||
1682 | 46 | cmd.append(packages) | ||
1683 | 47 | else: | ||
1684 | 48 | cmd.extend(packages) | ||
1685 | 49 | log("Installing {} with options: {}".format(packages, | ||
1686 | 50 | options)) | ||
1687 | 51 | _run_yum_command(cmd, fatal) | ||
1688 | 52 | |||
1689 | 53 | |||
1690 | 54 | def upgrade(options=None, fatal=False, dist=False): | ||
1691 | 55 | """Upgrade all packages.""" | ||
1692 | 56 | cmd = ['yum', '--assumeyes'] | ||
1693 | 57 | if options is not None: | ||
1694 | 58 | cmd.extend(options) | ||
1695 | 59 | cmd.append('upgrade') | ||
1696 | 60 | log("Upgrading with options: {}".format(options)) | ||
1697 | 61 | _run_yum_command(cmd, fatal) | ||
1698 | 62 | |||
1699 | 63 | |||
1700 | 64 | def update(fatal=False): | ||
1701 | 65 | """Update local yum cache.""" | ||
1702 | 66 | cmd = ['yum', '--assumeyes', 'update'] | ||
1703 | 67 | log("Update with fatal: {}".format(fatal)) | ||
1704 | 68 | _run_yum_command(cmd, fatal) | ||
1705 | 69 | |||
1706 | 70 | |||
1707 | 71 | def purge(packages, fatal=False): | ||
1708 | 72 | """Purge one or more packages.""" | ||
1709 | 73 | cmd = ['yum', '--assumeyes', 'remove'] | ||
1710 | 74 | if isinstance(packages, six.string_types): | ||
1711 | 75 | cmd.append(packages) | ||
1712 | 76 | else: | ||
1713 | 77 | cmd.extend(packages) | ||
1714 | 78 | log("Purging {}".format(packages)) | ||
1715 | 79 | _run_yum_command(cmd, fatal) | ||
1716 | 80 | |||
1717 | 81 | |||
1718 | 82 | def yum_search(packages): | ||
1719 | 83 | """Search for a package.""" | ||
1720 | 84 | output = {} | ||
1721 | 85 | cmd = ['yum', 'search'] | ||
1722 | 86 | if isinstance(packages, six.string_types): | ||
1723 | 87 | cmd.append(packages) | ||
1724 | 88 | else: | ||
1725 | 89 | cmd.extend(packages) | ||
1726 | 90 | log("Searching for {}".format(packages)) | ||
1727 | 91 | result = subprocess.check_output(cmd) | ||
1728 | 92 | for package in list(packages): | ||
1729 | 93 | output[package] = package in result | ||
1730 | 94 | return output | ||
1731 | 95 | |||
1732 | 96 | |||
1733 | 97 | def add_source(source, key=None): | ||
1734 | 98 | """Add a package source to this system. | ||
1735 | 99 | |||
1736 | 100 | @param source: a URL with a rpm package | ||
1737 | 101 | |||
1738 | 102 | @param key: A key to be added to the system's keyring and used | ||
1739 | 103 | to verify the signatures on packages. Ideally, this should be an | ||
1740 | 104 | ASCII format GPG public key including the block headers. A GPG key | ||
1741 | 105 | id may also be used, but be aware that only insecure protocols are | ||
1742 | 106 | available to retrieve the actual public key from a public keyserver | ||
1743 | 107 | placing your Juju environment at risk. | ||
1744 | 108 | """ | ||
1745 | 109 | if source is None: | ||
1746 | 110 | log('Source is not present. Skipping') | ||
1747 | 111 | return | ||
1748 | 112 | |||
1749 | 113 | if source.startswith('http'): | ||
1750 | 114 | directory = '/etc/yum.repos.d/' | ||
1751 | 115 | for filename in os.listdir(directory): | ||
1752 | 116 | with open(directory + filename, 'r') as rpm_file: | ||
1753 | 117 | if source in rpm_file.read(): | ||
1754 | 118 | break | ||
1755 | 119 | else: | ||
1756 | 120 | log("Add source: {!r}".format(source)) | ||
1757 | 121 | # write in the charms.repo | ||
1758 | 122 | with open(directory + 'Charms.repo', 'a') as rpm_file: | ||
1759 | 123 | rpm_file.write('[%s]\n' % source[7:].replace('/', '_')) | ||
1760 | 124 | rpm_file.write('name=%s\n' % source[7:]) | ||
1761 | 125 | rpm_file.write('baseurl=%s\n\n' % source) | ||
1762 | 126 | else: | ||
1763 | 127 | log("Unknown source: {!r}".format(source)) | ||
1764 | 128 | |||
1765 | 129 | if key: | ||
1766 | 130 | if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: | ||
1767 | 131 | with NamedTemporaryFile('w+') as key_file: | ||
1768 | 132 | key_file.write(key) | ||
1769 | 133 | key_file.flush() | ||
1770 | 134 | key_file.seek(0) | ||
1771 | 135 | subprocess.check_call(['rpm', '--import', key_file]) | ||
1772 | 136 | else: | ||
1773 | 137 | subprocess.check_call(['rpm', '--import', key]) | ||
1774 | 138 | |||
1775 | 139 | |||
1776 | 140 | def _run_yum_command(cmd, fatal=False): | ||
1777 | 141 | """Run an YUM command. | ||
1778 | 142 | |||
1779 | 143 | Checks the output and retry if the fatal flag is set to True. | ||
1780 | 144 | |||
1781 | 145 | :param: cmd: str: The yum command to run. | ||
1782 | 146 | :param: fatal: bool: Whether the command's output should be checked and | ||
1783 | 147 | retried. | ||
1784 | 148 | """ | ||
1785 | 149 | env = os.environ.copy() | ||
1786 | 150 | |||
1787 | 151 | if fatal: | ||
1788 | 152 | retry_count = 0 | ||
1789 | 153 | result = None | ||
1790 | 154 | |||
1791 | 155 | # If the command is considered "fatal", we need to retry if the yum | ||
1792 | 156 | # lock was not acquired. | ||
1793 | 157 | |||
1794 | 158 | while result is None or result == YUM_NO_LOCK: | ||
1795 | 159 | try: | ||
1796 | 160 | result = subprocess.check_call(cmd, env=env) | ||
1797 | 161 | except subprocess.CalledProcessError as e: | ||
1798 | 162 | retry_count = retry_count + 1 | ||
1799 | 163 | if retry_count > YUM_NO_LOCK_RETRY_COUNT: | ||
1800 | 164 | raise | ||
1801 | 165 | result = e.returncode | ||
1802 | 166 | log("Couldn't acquire YUM lock. Will retry in {} seconds." | ||
1803 | 167 | "".format(YUM_NO_LOCK_RETRY_DELAY)) | ||
1804 | 168 | time.sleep(YUM_NO_LOCK_RETRY_DELAY) | ||
1805 | 169 | |||
1806 | 170 | else: | ||
1807 | 171 | subprocess.call(cmd, env=env) | ||
1808 | diff --git a/hooks/charmhelpers/fetch/giturl.py b/hooks/charmhelpers/fetch/giturl.py | |||
1809 | index 65ed531..4cf21bc 100644 | |||
1810 | --- a/hooks/charmhelpers/fetch/giturl.py | |||
1811 | +++ b/hooks/charmhelpers/fetch/giturl.py | |||
1812 | @@ -1,18 +1,16 @@ | |||
1813 | 1 | # Copyright 2014-2015 Canonical Limited. | 1 | # Copyright 2014-2015 Canonical Limited. |
1814 | 2 | # | 2 | # |
1816 | 3 | # This file is part of charm-helpers. | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
1817 | 4 | # you may not use this file except in compliance with the License. | ||
1818 | 5 | # You may obtain a copy of the License at | ||
1819 | 4 | # | 6 | # |
1823 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
1821 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1822 | 7 | # published by the Free Software Foundation. | ||
1824 | 8 | # | 8 | # |
1832 | 9 | # charm-helpers is distributed in the hope that it will be useful, | 9 | # Unless required by applicable law or agreed to in writing, software |
1833 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
1834 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1835 | 12 | # GNU Lesser General Public License for more details. | 12 | # See the License for the specific language governing permissions and |
1836 | 13 | # | 13 | # limitations under the License. |
1830 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
1831 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1837 | 16 | 14 | ||
1838 | 17 | import os | 15 | import os |
1839 | 18 | from subprocess import check_call, CalledProcessError | 16 | from subprocess import check_call, CalledProcessError |
1840 | @@ -20,17 +18,18 @@ from charmhelpers.fetch import ( | |||
1841 | 20 | BaseFetchHandler, | 18 | BaseFetchHandler, |
1842 | 21 | UnhandledSource, | 19 | UnhandledSource, |
1843 | 22 | filter_installed_packages, | 20 | filter_installed_packages, |
1845 | 23 | apt_install, | 21 | install, |
1846 | 24 | ) | 22 | ) |
1847 | 25 | 23 | ||
1848 | 26 | if filter_installed_packages(['git']) != []: | 24 | if filter_installed_packages(['git']) != []: |
1850 | 27 | apt_install(['git']) | 25 | install(['git']) |
1851 | 28 | if filter_installed_packages(['git']) != []: | 26 | if filter_installed_packages(['git']) != []: |
1852 | 29 | raise NotImplementedError('Unable to install git') | 27 | raise NotImplementedError('Unable to install git') |
1853 | 30 | 28 | ||
1854 | 31 | 29 | ||
1855 | 32 | class GitUrlFetchHandler(BaseFetchHandler): | 30 | class GitUrlFetchHandler(BaseFetchHandler): |
1857 | 33 | """Handler for git branches via generic and github URLs""" | 31 | """Handler for git branches via generic and github URLs.""" |
1858 | 32 | |||
1859 | 34 | def can_handle(self, source): | 33 | def can_handle(self, source): |
1860 | 35 | url_parts = self.parse_url(source) | 34 | url_parts = self.parse_url(source) |
1861 | 36 | # TODO (mattyw) no support for ssh git@ yet | 35 | # TODO (mattyw) no support for ssh git@ yet |
1862 | diff --git a/hooks/charmhelpers/fetch/ubuntu.py b/hooks/charmhelpers/fetch/ubuntu.py | |||
1863 | 37 | new file mode 100644 | 36 | new file mode 100644 |
1864 | index 0000000..fce496b | |||
1865 | --- /dev/null | |||
1866 | +++ b/hooks/charmhelpers/fetch/ubuntu.py | |||
1867 | @@ -0,0 +1,336 @@ | |||
1868 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
1869 | 2 | # | ||
1870 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
1871 | 4 | # you may not use this file except in compliance with the License. | ||
1872 | 5 | # You may obtain a copy of the License at | ||
1873 | 6 | # | ||
1874 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 | ||
1875 | 8 | # | ||
1876 | 9 | # Unless required by applicable law or agreed to in writing, software | ||
1877 | 10 | # distributed under the License is distributed on an "AS IS" BASIS, | ||
1878 | 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
1879 | 12 | # See the License for the specific language governing permissions and | ||
1880 | 13 | # limitations under the License. | ||
1881 | 14 | |||
1882 | 15 | import os | ||
1883 | 16 | import six | ||
1884 | 17 | import time | ||
1885 | 18 | import subprocess | ||
1886 | 19 | |||
1887 | 20 | from tempfile import NamedTemporaryFile | ||
1888 | 21 | from charmhelpers.core.host import ( | ||
1889 | 22 | lsb_release | ||
1890 | 23 | ) | ||
1891 | 24 | from charmhelpers.core.hookenv import log | ||
1892 | 25 | from charmhelpers.fetch import SourceConfigError | ||
1893 | 26 | |||
1894 | 27 | CLOUD_ARCHIVE = """# Ubuntu Cloud Archive | ||
1895 | 28 | deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main | ||
1896 | 29 | """ | ||
1897 | 30 | |||
1898 | 31 | PROPOSED_POCKET = """# Proposed | ||
1899 | 32 | deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted | ||
1900 | 33 | """ | ||
1901 | 34 | |||
1902 | 35 | CLOUD_ARCHIVE_POCKETS = { | ||
1903 | 36 | # Folsom | ||
1904 | 37 | 'folsom': 'precise-updates/folsom', | ||
1905 | 38 | 'precise-folsom': 'precise-updates/folsom', | ||
1906 | 39 | 'precise-folsom/updates': 'precise-updates/folsom', | ||
1907 | 40 | 'precise-updates/folsom': 'precise-updates/folsom', | ||
1908 | 41 | 'folsom/proposed': 'precise-proposed/folsom', | ||
1909 | 42 | 'precise-folsom/proposed': 'precise-proposed/folsom', | ||
1910 | 43 | 'precise-proposed/folsom': 'precise-proposed/folsom', | ||
1911 | 44 | # Grizzly | ||
1912 | 45 | 'grizzly': 'precise-updates/grizzly', | ||
1913 | 46 | 'precise-grizzly': 'precise-updates/grizzly', | ||
1914 | 47 | 'precise-grizzly/updates': 'precise-updates/grizzly', | ||
1915 | 48 | 'precise-updates/grizzly': 'precise-updates/grizzly', | ||
1916 | 49 | 'grizzly/proposed': 'precise-proposed/grizzly', | ||
1917 | 50 | 'precise-grizzly/proposed': 'precise-proposed/grizzly', | ||
1918 | 51 | 'precise-proposed/grizzly': 'precise-proposed/grizzly', | ||
1919 | 52 | # Havana | ||
1920 | 53 | 'havana': 'precise-updates/havana', | ||
1921 | 54 | 'precise-havana': 'precise-updates/havana', | ||
1922 | 55 | 'precise-havana/updates': 'precise-updates/havana', | ||
1923 | 56 | 'precise-updates/havana': 'precise-updates/havana', | ||
1924 | 57 | 'havana/proposed': 'precise-proposed/havana', | ||
1925 | 58 | 'precise-havana/proposed': 'precise-proposed/havana', | ||
1926 | 59 | 'precise-proposed/havana': 'precise-proposed/havana', | ||
1927 | 60 | # Icehouse | ||
1928 | 61 | 'icehouse': 'precise-updates/icehouse', | ||
1929 | 62 | 'precise-icehouse': 'precise-updates/icehouse', | ||
1930 | 63 | 'precise-icehouse/updates': 'precise-updates/icehouse', | ||
1931 | 64 | 'precise-updates/icehouse': 'precise-updates/icehouse', | ||
1932 | 65 | 'icehouse/proposed': 'precise-proposed/icehouse', | ||
1933 | 66 | 'precise-icehouse/proposed': 'precise-proposed/icehouse', | ||
1934 | 67 | 'precise-proposed/icehouse': 'precise-proposed/icehouse', | ||
1935 | 68 | # Juno | ||
1936 | 69 | 'juno': 'trusty-updates/juno', | ||
1937 | 70 | 'trusty-juno': 'trusty-updates/juno', | ||
1938 | 71 | 'trusty-juno/updates': 'trusty-updates/juno', | ||
1939 | 72 | 'trusty-updates/juno': 'trusty-updates/juno', | ||
1940 | 73 | 'juno/proposed': 'trusty-proposed/juno', | ||
1941 | 74 | 'trusty-juno/proposed': 'trusty-proposed/juno', | ||
1942 | 75 | 'trusty-proposed/juno': 'trusty-proposed/juno', | ||
1943 | 76 | # Kilo | ||
1944 | 77 | 'kilo': 'trusty-updates/kilo', | ||
1945 | 78 | 'trusty-kilo': 'trusty-updates/kilo', | ||
1946 | 79 | 'trusty-kilo/updates': 'trusty-updates/kilo', | ||
1947 | 80 | 'trusty-updates/kilo': 'trusty-updates/kilo', | ||
1948 | 81 | 'kilo/proposed': 'trusty-proposed/kilo', | ||
1949 | 82 | 'trusty-kilo/proposed': 'trusty-proposed/kilo', | ||
1950 | 83 | 'trusty-proposed/kilo': 'trusty-proposed/kilo', | ||
1951 | 84 | # Liberty | ||
1952 | 85 | 'liberty': 'trusty-updates/liberty', | ||
1953 | 86 | 'trusty-liberty': 'trusty-updates/liberty', | ||
1954 | 87 | 'trusty-liberty/updates': 'trusty-updates/liberty', | ||
1955 | 88 | 'trusty-updates/liberty': 'trusty-updates/liberty', | ||
1956 | 89 | 'liberty/proposed': 'trusty-proposed/liberty', | ||
1957 | 90 | 'trusty-liberty/proposed': 'trusty-proposed/liberty', | ||
1958 | 91 | 'trusty-proposed/liberty': 'trusty-proposed/liberty', | ||
1959 | 92 | # Mitaka | ||
1960 | 93 | 'mitaka': 'trusty-updates/mitaka', | ||
1961 | 94 | 'trusty-mitaka': 'trusty-updates/mitaka', | ||
1962 | 95 | 'trusty-mitaka/updates': 'trusty-updates/mitaka', | ||
1963 | 96 | 'trusty-updates/mitaka': 'trusty-updates/mitaka', | ||
1964 | 97 | 'mitaka/proposed': 'trusty-proposed/mitaka', | ||
1965 | 98 | 'trusty-mitaka/proposed': 'trusty-proposed/mitaka', | ||
1966 | 99 | 'trusty-proposed/mitaka': 'trusty-proposed/mitaka', | ||
1967 | 100 | # Newton | ||
1968 | 101 | 'newton': 'xenial-updates/newton', | ||
1969 | 102 | 'xenial-newton': 'xenial-updates/newton', | ||
1970 | 103 | 'xenial-newton/updates': 'xenial-updates/newton', | ||
1971 | 104 | 'xenial-updates/newton': 'xenial-updates/newton', | ||
1972 | 105 | 'newton/proposed': 'xenial-proposed/newton', | ||
1973 | 106 | 'xenial-newton/proposed': 'xenial-proposed/newton', | ||
1974 | 107 | 'xenial-proposed/newton': 'xenial-proposed/newton', | ||
1975 | 108 | } | ||
1976 | 109 | |||
1977 | 110 | APT_NO_LOCK = 100 # The return code for "couldn't acquire lock" in APT. | ||
1978 | 111 | APT_NO_LOCK_RETRY_DELAY = 10 # Wait 10 seconds between apt lock checks. | ||
1979 | 112 | APT_NO_LOCK_RETRY_COUNT = 30 # Retry to acquire the lock X times. | ||
1980 | 113 | |||
1981 | 114 | |||
1982 | 115 | def filter_installed_packages(packages): | ||
1983 | 116 | """Return a list of packages that require installation.""" | ||
1984 | 117 | cache = apt_cache() | ||
1985 | 118 | _pkgs = [] | ||
1986 | 119 | for package in packages: | ||
1987 | 120 | try: | ||
1988 | 121 | p = cache[package] | ||
1989 | 122 | p.current_ver or _pkgs.append(package) | ||
1990 | 123 | except KeyError: | ||
1991 | 124 | log('Package {} has no installation candidate.'.format(package), | ||
1992 | 125 | level='WARNING') | ||
1993 | 126 | _pkgs.append(package) | ||
1994 | 127 | return _pkgs | ||
1995 | 128 | |||
1996 | 129 | |||
1997 | 130 | def apt_cache(in_memory=True, progress=None): | ||
1998 | 131 | """Build and return an apt cache.""" | ||
1999 | 132 | from apt import apt_pkg | ||
2000 | 133 | apt_pkg.init() | ||
2001 | 134 | if in_memory: | ||
2002 | 135 | apt_pkg.config.set("Dir::Cache::pkgcache", "") | ||
2003 | 136 | apt_pkg.config.set("Dir::Cache::srcpkgcache", "") | ||
2004 | 137 | return apt_pkg.Cache(progress) | ||
2005 | 138 | |||
2006 | 139 | |||
2007 | 140 | def install(packages, options=None, fatal=False): | ||
2008 | 141 | """Install one or more packages.""" | ||
2009 | 142 | if options is None: | ||
2010 | 143 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
2011 | 144 | |||
2012 | 145 | cmd = ['apt-get', '--assume-yes'] | ||
2013 | 146 | cmd.extend(options) | ||
2014 | 147 | cmd.append('install') | ||
2015 | 148 | if isinstance(packages, six.string_types): | ||
2016 | 149 | cmd.append(packages) | ||
2017 | 150 | else: | ||
2018 | 151 | cmd.extend(packages) | ||
2019 | 152 | log("Installing {} with options: {}".format(packages, | ||
2020 | 153 | options)) | ||
2021 | 154 | _run_apt_command(cmd, fatal) | ||
2022 | 155 | |||
2023 | 156 | |||
2024 | 157 | def upgrade(options=None, fatal=False, dist=False): | ||
2025 | 158 | """Upgrade all packages.""" | ||
2026 | 159 | if options is None: | ||
2027 | 160 | options = ['--option=Dpkg::Options::=--force-confold'] | ||
2028 | 161 | |||
2029 | 162 | cmd = ['apt-get', '--assume-yes'] | ||
2030 | 163 | cmd.extend(options) | ||
2031 | 164 | if dist: | ||
2032 | 165 | cmd.append('dist-upgrade') | ||
2033 | 166 | else: | ||
2034 | 167 | cmd.append('upgrade') | ||
2035 | 168 | log("Upgrading with options: {}".format(options)) | ||
2036 | 169 | _run_apt_command(cmd, fatal) | ||
2037 | 170 | |||
2038 | 171 | |||
2039 | 172 | def update(fatal=False): | ||
2040 | 173 | """Update local apt cache.""" | ||
2041 | 174 | cmd = ['apt-get', 'update'] | ||
2042 | 175 | _run_apt_command(cmd, fatal) | ||
2043 | 176 | |||
2044 | 177 | |||
2045 | 178 | def purge(packages, fatal=False): | ||
2046 | 179 | """Purge one or more packages.""" | ||
2047 | 180 | cmd = ['apt-get', '--assume-yes', 'purge'] | ||
2048 | 181 | if isinstance(packages, six.string_types): | ||
2049 | 182 | cmd.append(packages) | ||
2050 | 183 | else: | ||
2051 | 184 | cmd.extend(packages) | ||
2052 | 185 | log("Purging {}".format(packages)) | ||
2053 | 186 | _run_apt_command(cmd, fatal) | ||
2054 | 187 | |||
2055 | 188 | |||
2056 | 189 | def apt_mark(packages, mark, fatal=False): | ||
2057 | 190 | """Flag one or more packages using apt-mark.""" | ||
2058 | 191 | log("Marking {} as {}".format(packages, mark)) | ||
2059 | 192 | cmd = ['apt-mark', mark] | ||
2060 | 193 | if isinstance(packages, six.string_types): | ||
2061 | 194 | cmd.append(packages) | ||
2062 | 195 | else: | ||
2063 | 196 | cmd.extend(packages) | ||
2064 | 197 | |||
2065 | 198 | if fatal: | ||
2066 | 199 | subprocess.check_call(cmd, universal_newlines=True) | ||
2067 | 200 | else: | ||
2068 | 201 | subprocess.call(cmd, universal_newlines=True) | ||
2069 | 202 | |||
2070 | 203 | |||
2071 | 204 | def apt_hold(packages, fatal=False): | ||
2072 | 205 | return apt_mark(packages, 'hold', fatal=fatal) | ||
2073 | 206 | |||
2074 | 207 | |||
2075 | 208 | def apt_unhold(packages, fatal=False): | ||
2076 | 209 | return apt_mark(packages, 'unhold', fatal=fatal) | ||
2077 | 210 | |||
2078 | 211 | |||
2079 | 212 | def add_source(source, key=None): | ||
2080 | 213 | """Add a package source to this system. | ||
2081 | 214 | |||
2082 | 215 | @param source: a URL or sources.list entry, as supported by | ||
2083 | 216 | add-apt-repository(1). Examples:: | ||
2084 | 217 | |||
2085 | 218 | ppa:charmers/example | ||
2086 | 219 | deb https://stub:key@private.example.com/ubuntu trusty main | ||
2087 | 220 | |||
2088 | 221 | In addition: | ||
2089 | 222 | 'proposed:' may be used to enable the standard 'proposed' | ||
2090 | 223 | pocket for the release. | ||
2091 | 224 | 'cloud:' may be used to activate official cloud archive pockets, | ||
2092 | 225 | such as 'cloud:icehouse' | ||
2093 | 226 | 'distro' may be used as a noop | ||
2094 | 227 | |||
2095 | 228 | @param key: A key to be added to the system's APT keyring and used | ||
2096 | 229 | to verify the signatures on packages. Ideally, this should be an | ||
2097 | 230 | ASCII format GPG public key including the block headers. A GPG key | ||
2098 | 231 | id may also be used, but be aware that only insecure protocols are | ||
2099 | 232 | available to retrieve the actual public key from a public keyserver | ||
2100 | 233 | placing your Juju environment at risk. ppa and cloud archive keys | ||
2101 | 234 | are securely added automtically, so sould not be provided. | ||
2102 | 235 | """ | ||
2103 | 236 | if source is None: | ||
2104 | 237 | log('Source is not present. Skipping') | ||
2105 | 238 | return | ||
2106 | 239 | |||
2107 | 240 | if (source.startswith('ppa:') or | ||
2108 | 241 | source.startswith('http') or | ||
2109 | 242 | source.startswith('deb ') or | ||
2110 | 243 | source.startswith('cloud-archive:')): | ||
2111 | 244 | subprocess.check_call(['add-apt-repository', '--yes', source]) | ||
2112 | 245 | elif source.startswith('cloud:'): | ||
2113 | 246 | install(filter_installed_packages(['ubuntu-cloud-keyring']), | ||
2114 | 247 | fatal=True) | ||
2115 | 248 | pocket = source.split(':')[-1] | ||
2116 | 249 | if pocket not in CLOUD_ARCHIVE_POCKETS: | ||
2117 | 250 | raise SourceConfigError( | ||
2118 | 251 | 'Unsupported cloud: source option %s' % | ||
2119 | 252 | pocket) | ||
2120 | 253 | actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] | ||
2121 | 254 | with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: | ||
2122 | 255 | apt.write(CLOUD_ARCHIVE.format(actual_pocket)) | ||
2123 | 256 | elif source == 'proposed': | ||
2124 | 257 | release = lsb_release()['DISTRIB_CODENAME'] | ||
2125 | 258 | with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: | ||
2126 | 259 | apt.write(PROPOSED_POCKET.format(release)) | ||
2127 | 260 | elif source == 'distro': | ||
2128 | 261 | pass | ||
2129 | 262 | else: | ||
2130 | 263 | log("Unknown source: {!r}".format(source)) | ||
2131 | 264 | |||
2132 | 265 | if key: | ||
2133 | 266 | if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key: | ||
2134 | 267 | with NamedTemporaryFile('w+') as key_file: | ||
2135 | 268 | key_file.write(key) | ||
2136 | 269 | key_file.flush() | ||
2137 | 270 | key_file.seek(0) | ||
2138 | 271 | subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file) | ||
2139 | 272 | else: | ||
2140 | 273 | # Note that hkp: is in no way a secure protocol. Using a | ||
2141 | 274 | # GPG key id is pointless from a security POV unless you | ||
2142 | 275 | # absolutely trust your network and DNS. | ||
2143 | 276 | subprocess.check_call(['apt-key', 'adv', '--keyserver', | ||
2144 | 277 | 'hkp://keyserver.ubuntu.com:80', '--recv', | ||
2145 | 278 | key]) | ||
2146 | 279 | |||
2147 | 280 | |||
2148 | 281 | def _run_apt_command(cmd, fatal=False): | ||
2149 | 282 | """Run an APT command. | ||
2150 | 283 | |||
2151 | 284 | Checks the output and retries if the fatal flag is set | ||
2152 | 285 | to True. | ||
2153 | 286 | |||
2154 | 287 | :param: cmd: str: The apt command to run. | ||
2155 | 288 | :param: fatal: bool: Whether the command's output should be checked and | ||
2156 | 289 | retried. | ||
2157 | 290 | """ | ||
2158 | 291 | env = os.environ.copy() | ||
2159 | 292 | |||
2160 | 293 | if 'DEBIAN_FRONTEND' not in env: | ||
2161 | 294 | env['DEBIAN_FRONTEND'] = 'noninteractive' | ||
2162 | 295 | |||
2163 | 296 | if fatal: | ||
2164 | 297 | retry_count = 0 | ||
2165 | 298 | result = None | ||
2166 | 299 | |||
2167 | 300 | # If the command is considered "fatal", we need to retry if the apt | ||
2168 | 301 | # lock was not acquired. | ||
2169 | 302 | |||
2170 | 303 | while result is None or result == APT_NO_LOCK: | ||
2171 | 304 | try: | ||
2172 | 305 | result = subprocess.check_call(cmd, env=env) | ||
2173 | 306 | except subprocess.CalledProcessError as e: | ||
2174 | 307 | retry_count = retry_count + 1 | ||
2175 | 308 | if retry_count > APT_NO_LOCK_RETRY_COUNT: | ||
2176 | 309 | raise | ||
2177 | 310 | result = e.returncode | ||
2178 | 311 | log("Couldn't acquire DPKG lock. Will retry in {} seconds." | ||
2179 | 312 | "".format(APT_NO_LOCK_RETRY_DELAY)) | ||
2180 | 313 | time.sleep(APT_NO_LOCK_RETRY_DELAY) | ||
2181 | 314 | |||
2182 | 315 | else: | ||
2183 | 316 | subprocess.call(cmd, env=env) | ||
2184 | 317 | |||
2185 | 318 | |||
2186 | 319 | def get_upstream_version(package): | ||
2187 | 320 | """Determine upstream version based on installed package | ||
2188 | 321 | |||
2189 | 322 | @returns None (if not installed) or the upstream version | ||
2190 | 323 | """ | ||
2191 | 324 | import apt_pkg | ||
2192 | 325 | cache = apt_cache() | ||
2193 | 326 | try: | ||
2194 | 327 | pkg = cache[package] | ||
2195 | 328 | except: | ||
2196 | 329 | # the package is unknown to the current apt cache. | ||
2197 | 330 | return None | ||
2198 | 331 | |||
2199 | 332 | if not pkg.current_ver: | ||
2200 | 333 | # package is known, but no version is currently installed. | ||
2201 | 334 | return None | ||
2202 | 335 | |||
2203 | 336 | return apt_pkg.upstream_version(pkg.current_ver.ver_str) | ||
2204 | diff --git a/hooks/charmhelpers/osplatform.py b/hooks/charmhelpers/osplatform.py | |||
2205 | 0 | new file mode 100644 | 337 | new file mode 100644 |
2206 | index 0000000..ea490bb | |||
2207 | --- /dev/null | |||
2208 | +++ b/hooks/charmhelpers/osplatform.py | |||
2209 | @@ -0,0 +1,19 @@ | |||
2210 | 1 | import platform | ||
2211 | 2 | |||
2212 | 3 | |||
2213 | 4 | def get_platform(): | ||
2214 | 5 | """Return the current OS platform. | ||
2215 | 6 | |||
2216 | 7 | For example: if current os platform is Ubuntu then a string "ubuntu" | ||
2217 | 8 | will be returned (which is the name of the module). | ||
2218 | 9 | This string is used to decide which platform module should be imported. | ||
2219 | 10 | """ | ||
2220 | 11 | tuple_platform = platform.linux_distribution() | ||
2221 | 12 | current_platform = tuple_platform[0] | ||
2222 | 13 | if "Ubuntu" in current_platform: | ||
2223 | 14 | return "ubuntu" | ||
2224 | 15 | elif "CentOS" in current_platform: | ||
2225 | 16 | return "centos" | ||
2226 | 17 | else: | ||
2227 | 18 | raise RuntimeError("This module is not supported on {}." | ||
2228 | 19 | .format(current_platform)) | ||
2229 | diff --git a/hooks/ntpmaster_hooks.py b/hooks/ntpmaster_hooks.py | |||
2230 | index 979fbf0..1bbe8ce 100755 | |||
2231 | --- a/hooks/ntpmaster_hooks.py | |||
2232 | +++ b/hooks/ntpmaster_hooks.py | |||
2233 | @@ -65,8 +65,19 @@ def write_config(): | |||
2234 | 65 | shutil.copy(NTP_CONF_ORIG, NTP_CONF) | 65 | shutil.copy(NTP_CONF_ORIG, NTP_CONF) |
2235 | 66 | 66 | ||
2236 | 67 | 67 | ||
2237 | 68 | def assess_status(): | ||
2238 | 69 | hookenv.application_version_set( | ||
2239 | 70 | fetch.get_upstream_version('ntp') | ||
2240 | 71 | ) | ||
2241 | 72 | if host.service_running('ntp'): | ||
2242 | 73 | hookenv.status_set('active', 'Unit is ready') | ||
2243 | 74 | else: | ||
2244 | 75 | hookenv.status_set('blocked', 'ntp not running') | ||
2245 | 76 | |||
2246 | 77 | |||
2247 | 68 | if __name__ == '__main__': | 78 | if __name__ == '__main__': |
2248 | 69 | try: | 79 | try: |
2249 | 70 | hooks.execute(sys.argv) | 80 | hooks.execute(sys.argv) |
2250 | 71 | except UnregisteredHookError as e: | 81 | except UnregisteredHookError as e: |
2251 | 72 | hookenv.log('Unknown hook {} - skipping.'.format(e)) | 82 | hookenv.log('Unknown hook {} - skipping.'.format(e)) |
2252 | 83 | assess_status() | ||
2253 | diff --git a/hooks/update-status b/hooks/update-status | |||
2254 | 73 | new file mode 120000 | 84 | new file mode 120000 |
2255 | index 0000000..ce3b60a | |||
2256 | --- /dev/null | |||
2257 | +++ b/hooks/update-status | |||
2258 | @@ -0,0 +1 @@ | |||
2259 | 1 | ntpmaster_hooks.py | ||
2260 | 0 | \ No newline at end of file | 2 | \ No newline at end of file |
For reference:
Model Controller Cloud/Region Version
default lxd-localhost lxd/localhost 2.0.0
App Version Status Scale Charm Store Rev OS Notes
ntp 4.2.6.p5+dfsg active 2 ntp local 14 ubuntu
ntpmaster 4.2.6.p5+dfsg active 1 ntpmaster local 30 ubuntu
ubuntu 14.04 active 2 ubuntu jujucharms 8 ubuntu exposed
Unit Workload Agent Machine Public address Ports Message
ntpmaster/0* active idle 0 10.20.200.76 123/udp Unit is ready
ubuntu/0 active idle 1 10.20.200.25 ready
ntp/1 active idle 10.20.200.25 Unit is ready
ubuntu/1* active idle 2 10.20.200.38 ready
ntp/0* active idle 10.20.200.38 Unit is ready
Machine State DNS Inst id Series AZ
0 started 10.20.200.76 juju-983066-0 trusty
1 started 10.20.200.25 juju-983066-1 trusty
2 started 10.20.200.38 juju-983066-2 trusty
Relation Provides Consumes Type
ntp-peers ntp ntp peer
master ntp ntpmaster regular
juju-info ntp ubuntu regular
peer ntpmaster ntpmaster peer
juju-info ubuntu ntp subordinate