Merge lp:~1chb1n/charms/trusty/glance/next-amulet-debug-and-makefile into lp:~openstack-charmers-archive/charms/trusty/glance/trunk
- Trusty Tahr (14.04)
- next-amulet-debug-and-makefile
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~1chb1n/charms/trusty/glance/next-amulet-debug-and-makefile |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/glance/trunk |
Diff against target: |
4234 lines (+2604/-318) 57 files modified
.bzrignore (+1/-0) Makefile (+2/-3) README.md (+82/-0) actions.yaml (+2/-0) actions/git_reinstall.py (+45/-0) config.yaml (+22/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+41/-7) hooks/charmhelpers/contrib/hahelpers/cluster.py (+5/-1) hooks/charmhelpers/contrib/network/ip.py (+84/-1) hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+34/-5) hooks/charmhelpers/contrib/openstack/context.py (+280/-13) hooks/charmhelpers/contrib/openstack/files/__init__.py (+18/-0) hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh (+32/-0) hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh (+30/-0) hooks/charmhelpers/contrib/openstack/ip.py (+37/-0) hooks/charmhelpers/contrib/openstack/neutron.py (+83/-0) hooks/charmhelpers/contrib/openstack/templates/git.upstart (+17/-0) hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken (+9/-0) hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo (+22/-0) hooks/charmhelpers/contrib/openstack/templates/section-zeromq (+14/-0) hooks/charmhelpers/contrib/openstack/utils.py (+142/-141) hooks/charmhelpers/contrib/python/packages.py (+2/-2) hooks/charmhelpers/core/fstab.py (+4/-4) hooks/charmhelpers/core/hookenv.py (+40/-1) hooks/charmhelpers/core/host.py (+10/-6) hooks/charmhelpers/core/services/helpers.py (+12/-4) hooks/charmhelpers/core/strutils.py (+42/-0) hooks/charmhelpers/core/sysctl.py (+2/-2) hooks/charmhelpers/core/templating.py (+3/-3) hooks/charmhelpers/core/unitdata.py (+477/-0) hooks/charmhelpers/fetch/archiveurl.py (+10/-10) hooks/charmhelpers/fetch/giturl.py (+1/-1) hooks/glance_relations.py (+43/-17) hooks/glance_utils.py (+139/-9) templates/kilo/glance-api-paste.ini (+77/-0) templates/kilo/glance-api.conf (+83/-0) templates/kilo/glance-registry-paste.ini (+30/-0) templates/kilo/glance-registry.conf (+27/-0) templates/parts/keystone (+1/-0) templates/parts/section-database (+1/-0) tests/016-basic-trusty-juno (+11/-0) tests/017-basic-trusty-kilo (+11/-0) tests/018-basic-utopic-juno (+9/-0) tests/019-basic-vivid-kilo (+9/-0) tests/050-basic-trusty-icehouse-git (+9/-0) tests/051-basic-trusty-juno-git (+12/-0) tests/10-basic-precise-essex (+0/-9) tests/11-basic-precise-folsom (+0/-11) tests/12-basic-precise-grizzly (+0/-11) tests/13-basic-precise-havana (+0/-11) tests/basic_deployment.py (+27/-4) tests/charmhelpers/contrib/amulet/utils.py (+125/-3) tests/charmhelpers/contrib/openstack/amulet/deployment.py (+34/-5) unit_tests/__init__.py (+1/-0) unit_tests/test_actions_git_reinstall.py (+96/-0) unit_tests/test_glance_relations.py (+113/-26) unit_tests/test_glance_utils.py (+141/-8) |
To merge this branch: | bzr merge lp:~1chb1n/charms/trusty/glance/next-amulet-debug-and-makefile |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenStack Charmers | Pending | ||
Review via email: mp+256581@code.launchpad.net |
This proposal has been superseded by a proposal from 2015-04-17.
Commit message
Description of the change
auto normalize amulet test definitions and amulet make targets; charm-helper sync.
uosci-testing-bot (uosci-testing-bot) wrote : | # |
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #3326 glance for 1chb1n mp256581
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #3293 glance for 1chb1n mp256581
AMULET FAIL: amulet-test failed
AMULET Results (max last 2 lines):
make: *** [test] Error 1
ERROR:root:Make target returned non-zero.
Full amulet test output: http://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #3577 glance for 1chb1n mp256581
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #3365 glance for 1chb1n mp256581
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #3334 glance for 1chb1n mp256581
AMULET OK: passed
Build: http://
Unmerged revisions
Preview Diff
1 | === modified file '.bzrignore' | |||
2 | --- .bzrignore 2014-07-02 08:09:03 +0000 | |||
3 | +++ .bzrignore 2015-04-16 21:50:07 +0000 | |||
4 | @@ -1,2 +1,3 @@ | |||
5 | 1 | .coverage | 1 | .coverage |
6 | 2 | bin | 2 | bin |
7 | 3 | tags | ||
8 | 3 | 4 | ||
9 | === modified file 'Makefile' | |||
10 | --- Makefile 2014-10-08 20:18:38 +0000 | |||
11 | +++ Makefile 2015-04-16 21:50:07 +0000 | |||
12 | @@ -3,7 +3,7 @@ | |||
13 | 3 | 3 | ||
14 | 4 | lint: | 4 | lint: |
15 | 5 | @echo "Running flake8 tests: " | 5 | @echo "Running flake8 tests: " |
17 | 6 | @flake8 --exclude hooks/charmhelpers hooks unit_tests tests | 6 | @flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests |
18 | 7 | @echo "OK" | 7 | @echo "OK" |
19 | 8 | @echo "Running charm proof: " | 8 | @echo "Running charm proof: " |
20 | 9 | @charm proof | 9 | @charm proof |
21 | @@ -26,8 +26,7 @@ | |||
22 | 26 | # /!\ Note: The -v should only be temporary until Amulet sends | 26 | # /!\ Note: The -v should only be temporary until Amulet sends |
23 | 27 | # raise_status() messages to stderr: | 27 | # raise_status() messages to stderr: |
24 | 28 | # https://bugs.launchpad.net/amulet/+bug/1320357 | 28 | # https://bugs.launchpad.net/amulet/+bug/1320357 |
27 | 29 | @juju test -v -p AMULET_HTTP_PROXY --timeout 900 \ | 29 | @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700 |
26 | 30 | 00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse | ||
28 | 31 | 30 | ||
29 | 32 | publish: lint unit_test | 31 | publish: lint unit_test |
30 | 33 | bzr push lp:charms/glance | 32 | bzr push lp:charms/glance |
31 | 34 | 33 | ||
32 | === modified file 'README.md' | |||
33 | --- README.md 2013-09-26 10:22:09 +0000 | |||
34 | +++ README.md 2015-04-16 21:50:07 +0000 | |||
35 | @@ -81,6 +81,88 @@ | |||
36 | 81 | Note that Glance in this configuration must be used with either Ceph or | 81 | Note that Glance in this configuration must be used with either Ceph or |
37 | 82 | Swift providing backing image storage. | 82 | Swift providing backing image storage. |
38 | 83 | 83 | ||
39 | 84 | Deploying from source | ||
40 | 85 | --------------------- | ||
41 | 86 | |||
42 | 87 | The minimum openstack-origin-git config required to deploy from source is: | ||
43 | 88 | |||
44 | 89 | openstack-origin-git: | ||
45 | 90 | "repositories: | ||
46 | 91 | - {name: requirements, | ||
47 | 92 | repository: 'git://git.openstack.org/openstack/requirements', | ||
48 | 93 | branch: stable/juno} | ||
49 | 94 | - {name: glance, | ||
50 | 95 | repository: 'git://git.openstack.org/openstack/glance', | ||
51 | 96 | branch: stable/juno}" | ||
52 | 97 | |||
53 | 98 | Note that there are only two 'name' values the charm knows about: 'requirements' | ||
54 | 99 | and 'glance'. These repositories must correspond to these 'name' values. | ||
55 | 100 | Additionally, the requirements repository must be specified first and the | ||
56 | 101 | glance repository must be specified last. All other repostories are installed | ||
57 | 102 | in the order in which they are specified. | ||
58 | 103 | |||
59 | 104 | The following is a full list of current tip repos (may not be up-to-date): | ||
60 | 105 | |||
61 | 106 | openstack-origin-git: | ||
62 | 107 | "repositories: | ||
63 | 108 | - {name: requirements, | ||
64 | 109 | repository: 'git://git.openstack.org/openstack/requirements', | ||
65 | 110 | branch: master} | ||
66 | 111 | - {name: oslo-concurrency, | ||
67 | 112 | repository: 'git://git.openstack.org/openstack/oslo.concurrency', | ||
68 | 113 | branch: master} | ||
69 | 114 | - {name: oslo-config, | ||
70 | 115 | repository: 'git://git.openstack.org/openstack/oslo.config', | ||
71 | 116 | branch: master} | ||
72 | 117 | - {name: oslo-db, | ||
73 | 118 | repository: 'git://git.openstack.org/openstack/oslo.db', | ||
74 | 119 | branch: master} | ||
75 | 120 | - {name: oslo-i18n, | ||
76 | 121 | repository: 'git://git.openstack.org/openstack/oslo.i18n', | ||
77 | 122 | branch: master} | ||
78 | 123 | - {name: oslo-messaging, | ||
79 | 124 | repository: 'git://git.openstack.org/openstack/oslo.messaging', | ||
80 | 125 | branch: master} | ||
81 | 126 | - {name: oslo-serialization, | ||
82 | 127 | repository: 'git://git.openstack.org/openstack/oslo.serialization', | ||
83 | 128 | branch: master} | ||
84 | 129 | - {name: oslo-utils, | ||
85 | 130 | repository: 'git://git.openstack.org/openstack/oslo.utils', | ||
86 | 131 | branch: master} | ||
87 | 132 | - {name: oslo-vmware, | ||
88 | 133 | repository: 'git://git.openstack.org/openstack/oslo.vmware', | ||
89 | 134 | branch: master} | ||
90 | 135 | - {name: osprofiler, | ||
91 | 136 | repository: 'git://git.openstack.org/stackforge/osprofiler', | ||
92 | 137 | branch: master} | ||
93 | 138 | - {name: pbr, | ||
94 | 139 | repository: 'git://git.openstack.org/openstack-dev/pbr', | ||
95 | 140 | branch: master} | ||
96 | 141 | - {name: python-keystoneclient, | ||
97 | 142 | repository: 'git://git.openstack.org/openstack/python-keystoneclient', | ||
98 | 143 | branch: master} | ||
99 | 144 | - {name: python-swiftclient, | ||
100 | 145 | repository: 'git://git.openstack.org/openstack/python-swiftclient', | ||
101 | 146 | branch: master} | ||
102 | 147 | - {name: sqlalchemy-migrate, | ||
103 | 148 | repository: 'git://git.openstack.org/stackforge/sqlalchemy-migrate', | ||
104 | 149 | branch: master} | ||
105 | 150 | - {name: stevedore, | ||
106 | 151 | repository: 'git://git.openstack.org/openstack/stevedore', | ||
107 | 152 | branch: master} | ||
108 | 153 | - {name: wsme, | ||
109 | 154 | repository: 'git://git.openstack.org/stackforge/wsme', | ||
110 | 155 | branch: master} | ||
111 | 156 | - {name: keystonemiddleware, | ||
112 | 157 | repository: 'git://git.openstack.org/openstack/keystonemiddleware', | ||
113 | 158 | branch: master} | ||
114 | 159 | - {name: glance-store, | ||
115 | 160 | repository: 'git://git.openstack.org/openstack/glance_store', | ||
116 | 161 | branch: master} | ||
117 | 162 | - {name: glance, | ||
118 | 163 | repository: 'git://git.openstack.org/openstack/glance', | ||
119 | 164 | branch: master}" | ||
120 | 165 | |||
121 | 84 | Contact Information | 166 | Contact Information |
122 | 85 | ------------------- | 167 | ------------------- |
123 | 86 | 168 | ||
124 | 87 | 169 | ||
125 | === added directory 'actions' | |||
126 | === added file 'actions.yaml' | |||
127 | --- actions.yaml 1970-01-01 00:00:00 +0000 | |||
128 | +++ actions.yaml 2015-04-16 21:50:07 +0000 | |||
129 | @@ -0,0 +1,2 @@ | |||
130 | 1 | git-reinstall: | ||
131 | 2 | description: Reinstall glance from the openstack-origin-git repositories. | ||
132 | 0 | 3 | ||
133 | === added symlink 'actions/git-reinstall' | |||
134 | === target is u'git_reinstall.py' | |||
135 | === added file 'actions/git_reinstall.py' | |||
136 | --- actions/git_reinstall.py 1970-01-01 00:00:00 +0000 | |||
137 | +++ actions/git_reinstall.py 2015-04-16 21:50:07 +0000 | |||
138 | @@ -0,0 +1,45 @@ | |||
139 | 1 | #!/usr/bin/python | ||
140 | 2 | import sys | ||
141 | 3 | import traceback | ||
142 | 4 | |||
143 | 5 | sys.path.append('hooks/') | ||
144 | 6 | |||
145 | 7 | from charmhelpers.contrib.openstack.utils import ( | ||
146 | 8 | git_install_requested, | ||
147 | 9 | ) | ||
148 | 10 | |||
149 | 11 | from charmhelpers.core.hookenv import ( | ||
150 | 12 | action_set, | ||
151 | 13 | action_fail, | ||
152 | 14 | config, | ||
153 | 15 | ) | ||
154 | 16 | |||
155 | 17 | from glance_utils import ( | ||
156 | 18 | git_install, | ||
157 | 19 | ) | ||
158 | 20 | |||
159 | 21 | from glance_relations import ( | ||
160 | 22 | config_changed, | ||
161 | 23 | ) | ||
162 | 24 | |||
163 | 25 | |||
164 | 26 | def git_reinstall(): | ||
165 | 27 | """Reinstall from source and restart services. | ||
166 | 28 | |||
167 | 29 | If the openstack-origin-git config option was used to install openstack | ||
168 | 30 | from source git repositories, then this action can be used to reinstall | ||
169 | 31 | from updated git repositories, followed by a restart of services.""" | ||
170 | 32 | if not git_install_requested(): | ||
171 | 33 | action_fail('openstack-origin-git is not configured') | ||
172 | 34 | return | ||
173 | 35 | |||
174 | 36 | try: | ||
175 | 37 | git_install(config('openstack-origin-git')) | ||
176 | 38 | config_changed() | ||
177 | 39 | except: | ||
178 | 40 | action_set({'traceback': traceback.format_exc()}) | ||
179 | 41 | action_fail('git-reinstall resulted in an unexpected error') | ||
180 | 42 | |||
181 | 43 | |||
182 | 44 | if __name__ == '__main__': | ||
183 | 45 | git_reinstall() | ||
184 | 0 | 46 | ||
185 | === modified file 'config.yaml' | |||
186 | --- config.yaml 2015-01-21 14:38:50 +0000 | |||
187 | +++ config.yaml 2015-04-16 21:50:07 +0000 | |||
188 | @@ -14,6 +14,22 @@ | |||
189 | 14 | Note that updating this setting to a source that is known to | 14 | Note that updating this setting to a source that is known to |
190 | 15 | provide a later version of OpenStack will trigger a software | 15 | provide a later version of OpenStack will trigger a software |
191 | 16 | upgrade. | 16 | upgrade. |
192 | 17 | |||
193 | 18 | Note that when openstack-origin-git is specified, openstack | ||
194 | 19 | specific packages will be installed from source rather than | ||
195 | 20 | from the openstack-origin repository. | ||
196 | 21 | openstack-origin-git: | ||
197 | 22 | default: | ||
198 | 23 | type: string | ||
199 | 24 | description: | | ||
200 | 25 | Specifies a YAML-formatted dictionary listing the git | ||
201 | 26 | repositories and branches from which to install OpenStack and | ||
202 | 27 | its dependencies. | ||
203 | 28 | |||
204 | 29 | Note that the installed config files will be determined based on | ||
205 | 30 | the OpenStack release of the openstack-origin option. | ||
206 | 31 | |||
207 | 32 | For more details see README.md. | ||
208 | 17 | database-user: | 33 | database-user: |
209 | 18 | default: glance | 34 | default: glance |
210 | 19 | type: string | 35 | type: string |
211 | @@ -189,4 +205,10 @@ | |||
212 | 189 | juju-myservice-0 | 205 | juju-myservice-0 |
213 | 190 | If you're running multiple environments with the same services in them | 206 | If you're running multiple environments with the same services in them |
214 | 191 | this allows you to differentiate between them. | 207 | this allows you to differentiate between them. |
215 | 208 | nagios_servicegroups: | ||
216 | 209 | default: "" | ||
217 | 210 | type: string | ||
218 | 211 | description: | | ||
219 | 212 | A comma-separated list of nagios servicegroups. | ||
220 | 213 | If left empty, the nagios_context will be used as the servicegroup | ||
221 | 192 | 214 | ||
222 | 193 | 215 | ||
223 | === modified file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
224 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-01-26 09:45:23 +0000 | |||
225 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-04-16 21:50:07 +0000 | |||
226 | @@ -24,6 +24,8 @@ | |||
227 | 24 | import pwd | 24 | import pwd |
228 | 25 | import grp | 25 | import grp |
229 | 26 | import os | 26 | import os |
230 | 27 | import glob | ||
231 | 28 | import shutil | ||
232 | 27 | import re | 29 | import re |
233 | 28 | import shlex | 30 | import shlex |
234 | 29 | import yaml | 31 | import yaml |
235 | @@ -161,7 +163,7 @@ | |||
236 | 161 | log('Check command not found: {}'.format(parts[0])) | 163 | log('Check command not found: {}'.format(parts[0])) |
237 | 162 | return '' | 164 | return '' |
238 | 163 | 165 | ||
240 | 164 | def write(self, nagios_context, hostname, nagios_servicegroups=None): | 166 | def write(self, nagios_context, hostname, nagios_servicegroups): |
241 | 165 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | 167 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( |
242 | 166 | self.command) | 168 | self.command) |
243 | 167 | with open(nrpe_check_file, 'w') as nrpe_check_config: | 169 | with open(nrpe_check_file, 'w') as nrpe_check_config: |
244 | @@ -177,14 +179,11 @@ | |||
245 | 177 | nagios_servicegroups) | 179 | nagios_servicegroups) |
246 | 178 | 180 | ||
247 | 179 | def write_service_config(self, nagios_context, hostname, | 181 | def write_service_config(self, nagios_context, hostname, |
249 | 180 | nagios_servicegroups=None): | 182 | nagios_servicegroups): |
250 | 181 | for f in os.listdir(NRPE.nagios_exportdir): | 183 | for f in os.listdir(NRPE.nagios_exportdir): |
251 | 182 | if re.search('.*{}.cfg'.format(self.command), f): | 184 | if re.search('.*{}.cfg'.format(self.command), f): |
252 | 183 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | 185 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) |
253 | 184 | 186 | ||
254 | 185 | if not nagios_servicegroups: | ||
255 | 186 | nagios_servicegroups = nagios_context | ||
256 | 187 | |||
257 | 188 | templ_vars = { | 187 | templ_vars = { |
258 | 189 | 'nagios_hostname': hostname, | 188 | 'nagios_hostname': hostname, |
259 | 190 | 'nagios_servicegroup': nagios_servicegroups, | 189 | 'nagios_servicegroup': nagios_servicegroups, |
260 | @@ -211,10 +210,10 @@ | |||
261 | 211 | super(NRPE, self).__init__() | 210 | super(NRPE, self).__init__() |
262 | 212 | self.config = config() | 211 | self.config = config() |
263 | 213 | self.nagios_context = self.config['nagios_context'] | 212 | self.nagios_context = self.config['nagios_context'] |
265 | 214 | if 'nagios_servicegroups' in self.config: | 213 | if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']: |
266 | 215 | self.nagios_servicegroups = self.config['nagios_servicegroups'] | 214 | self.nagios_servicegroups = self.config['nagios_servicegroups'] |
267 | 216 | else: | 215 | else: |
269 | 217 | self.nagios_servicegroups = 'juju' | 216 | self.nagios_servicegroups = self.nagios_context |
270 | 218 | self.unit_name = local_unit().replace('/', '-') | 217 | self.unit_name = local_unit().replace('/', '-') |
271 | 219 | if hostname: | 218 | if hostname: |
272 | 220 | self.hostname = hostname | 219 | self.hostname = hostname |
273 | @@ -322,3 +321,38 @@ | |||
274 | 322 | check_cmd='check_status_file.py -f ' | 321 | check_cmd='check_status_file.py -f ' |
275 | 323 | '/var/lib/nagios/service-check-%s.txt' % svc, | 322 | '/var/lib/nagios/service-check-%s.txt' % svc, |
276 | 324 | ) | 323 | ) |
277 | 324 | |||
278 | 325 | |||
279 | 326 | def copy_nrpe_checks(): | ||
280 | 327 | """ | ||
281 | 328 | Copy the nrpe checks into place | ||
282 | 329 | |||
283 | 330 | """ | ||
284 | 331 | NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' | ||
285 | 332 | nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks', | ||
286 | 333 | 'charmhelpers', 'contrib', 'openstack', | ||
287 | 334 | 'files') | ||
288 | 335 | |||
289 | 336 | if not os.path.exists(NAGIOS_PLUGINS): | ||
290 | 337 | os.makedirs(NAGIOS_PLUGINS) | ||
291 | 338 | for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")): | ||
292 | 339 | if os.path.isfile(fname): | ||
293 | 340 | shutil.copy2(fname, | ||
294 | 341 | os.path.join(NAGIOS_PLUGINS, os.path.basename(fname))) | ||
295 | 342 | |||
296 | 343 | |||
297 | 344 | def add_haproxy_checks(nrpe, unit_name): | ||
298 | 345 | """ | ||
299 | 346 | Add checks for each service in list | ||
300 | 347 | |||
301 | 348 | :param NRPE nrpe: NRPE object to add check to | ||
302 | 349 | :param str unit_name: Unit name to use in check description | ||
303 | 350 | """ | ||
304 | 351 | nrpe.add_check( | ||
305 | 352 | shortname='haproxy_servers', | ||
306 | 353 | description='Check HAProxy {%s}' % unit_name, | ||
307 | 354 | check_cmd='check_haproxy.sh') | ||
308 | 355 | nrpe.add_check( | ||
309 | 356 | shortname='haproxy_queue', | ||
310 | 357 | description='Check HAProxy queue depth {%s}' % unit_name, | ||
311 | 358 | check_cmd='check_haproxy_queue_depth.sh') | ||
312 | 325 | 359 | ||
313 | === modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py' | |||
314 | --- hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-01-26 09:45:23 +0000 | |||
315 | +++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-04-16 21:50:07 +0000 | |||
316 | @@ -48,6 +48,9 @@ | |||
317 | 48 | from charmhelpers.core.decorators import ( | 48 | from charmhelpers.core.decorators import ( |
318 | 49 | retry_on_exception, | 49 | retry_on_exception, |
319 | 50 | ) | 50 | ) |
320 | 51 | from charmhelpers.core.strutils import ( | ||
321 | 52 | bool_from_string, | ||
322 | 53 | ) | ||
323 | 51 | 54 | ||
324 | 52 | 55 | ||
325 | 53 | class HAIncompleteConfig(Exception): | 56 | class HAIncompleteConfig(Exception): |
326 | @@ -164,7 +167,8 @@ | |||
327 | 164 | . | 167 | . |
328 | 165 | returns: boolean | 168 | returns: boolean |
329 | 166 | ''' | 169 | ''' |
331 | 167 | if config_get('use-https') == "yes": | 170 | use_https = config_get('use-https') |
332 | 171 | if use_https and bool_from_string(use_https): | ||
333 | 168 | return True | 172 | return True |
334 | 169 | if config_get('ssl_cert') and config_get('ssl_key'): | 173 | if config_get('ssl_cert') and config_get('ssl_key'): |
335 | 170 | return True | 174 | return True |
336 | 171 | 175 | ||
337 | === modified file 'hooks/charmhelpers/contrib/network/ip.py' | |||
338 | --- hooks/charmhelpers/contrib/network/ip.py 2015-01-26 09:45:23 +0000 | |||
339 | +++ hooks/charmhelpers/contrib/network/ip.py 2015-04-16 21:50:07 +0000 | |||
340 | @@ -17,13 +17,16 @@ | |||
341 | 17 | import glob | 17 | import glob |
342 | 18 | import re | 18 | import re |
343 | 19 | import subprocess | 19 | import subprocess |
344 | 20 | import six | ||
345 | 21 | import socket | ||
346 | 20 | 22 | ||
347 | 21 | from functools import partial | 23 | from functools import partial |
348 | 22 | 24 | ||
349 | 23 | from charmhelpers.core.hookenv import unit_get | 25 | from charmhelpers.core.hookenv import unit_get |
350 | 24 | from charmhelpers.fetch import apt_install | 26 | from charmhelpers.fetch import apt_install |
351 | 25 | from charmhelpers.core.hookenv import ( | 27 | from charmhelpers.core.hookenv import ( |
353 | 26 | log | 28 | log, |
354 | 29 | WARNING, | ||
355 | 27 | ) | 30 | ) |
356 | 28 | 31 | ||
357 | 29 | try: | 32 | try: |
358 | @@ -365,3 +368,83 @@ | |||
359 | 365 | return True | 368 | return True |
360 | 366 | 369 | ||
361 | 367 | return False | 370 | return False |
362 | 371 | |||
363 | 372 | |||
364 | 373 | def is_ip(address): | ||
365 | 374 | """ | ||
366 | 375 | Returns True if address is a valid IP address. | ||
367 | 376 | """ | ||
368 | 377 | try: | ||
369 | 378 | # Test to see if already an IPv4 address | ||
370 | 379 | socket.inet_aton(address) | ||
371 | 380 | return True | ||
372 | 381 | except socket.error: | ||
373 | 382 | return False | ||
374 | 383 | |||
375 | 384 | |||
376 | 385 | def ns_query(address): | ||
377 | 386 | try: | ||
378 | 387 | import dns.resolver | ||
379 | 388 | except ImportError: | ||
380 | 389 | apt_install('python-dnspython') | ||
381 | 390 | import dns.resolver | ||
382 | 391 | |||
383 | 392 | if isinstance(address, dns.name.Name): | ||
384 | 393 | rtype = 'PTR' | ||
385 | 394 | elif isinstance(address, six.string_types): | ||
386 | 395 | rtype = 'A' | ||
387 | 396 | else: | ||
388 | 397 | return None | ||
389 | 398 | |||
390 | 399 | answers = dns.resolver.query(address, rtype) | ||
391 | 400 | if answers: | ||
392 | 401 | return str(answers[0]) | ||
393 | 402 | return None | ||
394 | 403 | |||
395 | 404 | |||
396 | 405 | def get_host_ip(hostname, fallback=None): | ||
397 | 406 | """ | ||
398 | 407 | Resolves the IP for a given hostname, or returns | ||
399 | 408 | the input if it is already an IP. | ||
400 | 409 | """ | ||
401 | 410 | if is_ip(hostname): | ||
402 | 411 | return hostname | ||
403 | 412 | |||
404 | 413 | ip_addr = ns_query(hostname) | ||
405 | 414 | if not ip_addr: | ||
406 | 415 | try: | ||
407 | 416 | ip_addr = socket.gethostbyname(hostname) | ||
408 | 417 | except: | ||
409 | 418 | log("Failed to resolve hostname '%s'" % (hostname), | ||
410 | 419 | level=WARNING) | ||
411 | 420 | return fallback | ||
412 | 421 | return ip_addr | ||
413 | 422 | |||
414 | 423 | |||
415 | 424 | def get_hostname(address, fqdn=True): | ||
416 | 425 | """ | ||
417 | 426 | Resolves hostname for given IP, or returns the input | ||
418 | 427 | if it is already a hostname. | ||
419 | 428 | """ | ||
420 | 429 | if is_ip(address): | ||
421 | 430 | try: | ||
422 | 431 | import dns.reversename | ||
423 | 432 | except ImportError: | ||
424 | 433 | apt_install("python-dnspython") | ||
425 | 434 | import dns.reversename | ||
426 | 435 | |||
427 | 436 | rev = dns.reversename.from_address(address) | ||
428 | 437 | result = ns_query(rev) | ||
429 | 438 | if not result: | ||
430 | 439 | return None | ||
431 | 440 | else: | ||
432 | 441 | result = address | ||
433 | 442 | |||
434 | 443 | if fqdn: | ||
435 | 444 | # strip trailing . | ||
436 | 445 | if result.endswith('.'): | ||
437 | 446 | return result[:-1] | ||
438 | 447 | else: | ||
439 | 448 | return result | ||
440 | 449 | else: | ||
441 | 450 | return result.split('.')[0] | ||
442 | 368 | 451 | ||
443 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
444 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:45:23 +0000 | |||
445 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-04-16 21:50:07 +0000 | |||
446 | @@ -15,6 +15,7 @@ | |||
447 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
448 | 16 | 16 | ||
449 | 17 | import six | 17 | import six |
450 | 18 | from collections import OrderedDict | ||
451 | 18 | from charmhelpers.contrib.amulet.deployment import ( | 19 | from charmhelpers.contrib.amulet.deployment import ( |
452 | 19 | AmuletDeployment | 20 | AmuletDeployment |
453 | 20 | ) | 21 | ) |
454 | @@ -43,7 +44,7 @@ | |||
455 | 43 | Determine if the local branch being tested is derived from its | 44 | Determine if the local branch being tested is derived from its |
456 | 44 | stable or next (dev) branch, and based on this, use the corresonding | 45 | stable or next (dev) branch, and based on this, use the corresonding |
457 | 45 | stable or next branches for the other_services.""" | 46 | stable or next branches for the other_services.""" |
459 | 46 | base_charms = ['mysql', 'mongodb', 'rabbitmq-server'] | 47 | base_charms = ['mysql', 'mongodb'] |
460 | 47 | 48 | ||
461 | 48 | if self.stable: | 49 | if self.stable: |
462 | 49 | for svc in other_services: | 50 | for svc in other_services: |
463 | @@ -71,16 +72,19 @@ | |||
464 | 71 | services.append(this_service) | 72 | services.append(this_service) |
465 | 72 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', | 73 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
466 | 73 | 'ceph-osd', 'ceph-radosgw'] | 74 | 'ceph-osd', 'ceph-radosgw'] |
467 | 75 | # Openstack subordinate charms do not expose an origin option as that | ||
468 | 76 | # is controlled by the principle | ||
469 | 77 | ignore = ['neutron-openvswitch'] | ||
470 | 74 | 78 | ||
471 | 75 | if self.openstack: | 79 | if self.openstack: |
472 | 76 | for svc in services: | 80 | for svc in services: |
474 | 77 | if svc['name'] not in use_source: | 81 | if svc['name'] not in use_source + ignore: |
475 | 78 | config = {'openstack-origin': self.openstack} | 82 | config = {'openstack-origin': self.openstack} |
476 | 79 | self.d.configure(svc['name'], config) | 83 | self.d.configure(svc['name'], config) |
477 | 80 | 84 | ||
478 | 81 | if self.source: | 85 | if self.source: |
479 | 82 | for svc in services: | 86 | for svc in services: |
481 | 83 | if svc['name'] in use_source: | 87 | if svc['name'] in use_source and svc['name'] not in ignore: |
482 | 84 | config = {'source': self.source} | 88 | config = {'source': self.source} |
483 | 85 | self.d.configure(svc['name'], config) | 89 | self.d.configure(svc['name'], config) |
484 | 86 | 90 | ||
485 | @@ -97,12 +101,37 @@ | |||
486 | 97 | """ | 101 | """ |
487 | 98 | (self.precise_essex, self.precise_folsom, self.precise_grizzly, | 102 | (self.precise_essex, self.precise_folsom, self.precise_grizzly, |
488 | 99 | self.precise_havana, self.precise_icehouse, | 103 | self.precise_havana, self.precise_icehouse, |
490 | 100 | self.trusty_icehouse) = range(6) | 104 | self.trusty_icehouse, self.trusty_juno, self.trusty_kilo, |
491 | 105 | self.utopic_juno, self.vivid_kilo) = range(10) | ||
492 | 101 | releases = { | 106 | releases = { |
493 | 102 | ('precise', None): self.precise_essex, | 107 | ('precise', None): self.precise_essex, |
494 | 103 | ('precise', 'cloud:precise-folsom'): self.precise_folsom, | 108 | ('precise', 'cloud:precise-folsom'): self.precise_folsom, |
495 | 104 | ('precise', 'cloud:precise-grizzly'): self.precise_grizzly, | 109 | ('precise', 'cloud:precise-grizzly'): self.precise_grizzly, |
496 | 105 | ('precise', 'cloud:precise-havana'): self.precise_havana, | 110 | ('precise', 'cloud:precise-havana'): self.precise_havana, |
497 | 106 | ('precise', 'cloud:precise-icehouse'): self.precise_icehouse, | 111 | ('precise', 'cloud:precise-icehouse'): self.precise_icehouse, |
499 | 107 | ('trusty', None): self.trusty_icehouse} | 112 | ('trusty', None): self.trusty_icehouse, |
500 | 113 | ('trusty', 'cloud:trusty-juno'): self.trusty_juno, | ||
501 | 114 | ('trusty', 'cloud:trusty-kilo'): self.trusty_kilo, | ||
502 | 115 | ('utopic', None): self.utopic_juno, | ||
503 | 116 | ('vivid', None): self.vivid_kilo} | ||
504 | 108 | return releases[(self.series, self.openstack)] | 117 | return releases[(self.series, self.openstack)] |
505 | 118 | |||
506 | 119 | def _get_openstack_release_string(self): | ||
507 | 120 | """Get openstack release string. | ||
508 | 121 | |||
509 | 122 | Return a string representing the openstack release. | ||
510 | 123 | """ | ||
511 | 124 | releases = OrderedDict([ | ||
512 | 125 | ('precise', 'essex'), | ||
513 | 126 | ('quantal', 'folsom'), | ||
514 | 127 | ('raring', 'grizzly'), | ||
515 | 128 | ('saucy', 'havana'), | ||
516 | 129 | ('trusty', 'icehouse'), | ||
517 | 130 | ('utopic', 'juno'), | ||
518 | 131 | ('vivid', 'kilo'), | ||
519 | 132 | ]) | ||
520 | 133 | if self.openstack: | ||
521 | 134 | os_origin = self.openstack.split(':')[1] | ||
522 | 135 | return os_origin.split('%s-' % self.series)[1].split('/')[0] | ||
523 | 136 | else: | ||
524 | 137 | return releases[self.series] | ||
525 | 109 | 138 | ||
526 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' | |||
527 | --- hooks/charmhelpers/contrib/openstack/context.py 2015-03-19 21:56:40 +0000 | |||
528 | +++ hooks/charmhelpers/contrib/openstack/context.py 2015-04-16 21:50:07 +0000 | |||
529 | @@ -16,11 +16,13 @@ | |||
530 | 16 | 16 | ||
531 | 17 | import json | 17 | import json |
532 | 18 | import os | 18 | import os |
533 | 19 | import re | ||
534 | 19 | import time | 20 | import time |
535 | 20 | from base64 import b64decode | 21 | from base64 import b64decode |
536 | 21 | from subprocess import check_call | 22 | from subprocess import check_call |
537 | 22 | 23 | ||
538 | 23 | import six | 24 | import six |
539 | 25 | import yaml | ||
540 | 24 | 26 | ||
541 | 25 | from charmhelpers.fetch import ( | 27 | from charmhelpers.fetch import ( |
542 | 26 | apt_install, | 28 | apt_install, |
543 | @@ -45,8 +47,11 @@ | |||
544 | 45 | ) | 47 | ) |
545 | 46 | 48 | ||
546 | 47 | from charmhelpers.core.sysctl import create as sysctl_create | 49 | from charmhelpers.core.sysctl import create as sysctl_create |
547 | 50 | from charmhelpers.core.strutils import bool_from_string | ||
548 | 48 | 51 | ||
549 | 49 | from charmhelpers.core.host import ( | 52 | from charmhelpers.core.host import ( |
550 | 53 | list_nics, | ||
551 | 54 | get_nic_hwaddr, | ||
552 | 50 | mkdir, | 55 | mkdir, |
553 | 51 | write_file, | 56 | write_file, |
554 | 52 | ) | 57 | ) |
555 | @@ -63,6 +68,11 @@ | |||
556 | 63 | ) | 68 | ) |
557 | 64 | from charmhelpers.contrib.openstack.neutron import ( | 69 | from charmhelpers.contrib.openstack.neutron import ( |
558 | 65 | neutron_plugin_attribute, | 70 | neutron_plugin_attribute, |
559 | 71 | parse_data_port_mappings, | ||
560 | 72 | ) | ||
561 | 73 | from charmhelpers.contrib.openstack.ip import ( | ||
562 | 74 | resolve_address, | ||
563 | 75 | INTERNAL, | ||
564 | 66 | ) | 76 | ) |
565 | 67 | from charmhelpers.contrib.openstack.ip import ( | 77 | from charmhelpers.contrib.openstack.ip import ( |
566 | 68 | resolve_address, | 78 | resolve_address, |
567 | @@ -70,13 +80,14 @@ | |||
568 | 70 | ) | 80 | ) |
569 | 71 | from charmhelpers.contrib.network.ip import ( | 81 | from charmhelpers.contrib.network.ip import ( |
570 | 72 | get_address_in_network, | 82 | get_address_in_network, |
571 | 83 | get_ipv4_addr, | ||
572 | 73 | get_ipv6_addr, | 84 | get_ipv6_addr, |
573 | 74 | get_netmask_for_address, | 85 | get_netmask_for_address, |
574 | 75 | format_ipv6_addr, | 86 | format_ipv6_addr, |
575 | 76 | is_address_in_network, | 87 | is_address_in_network, |
576 | 88 | is_bridge_member, | ||
577 | 77 | ) | 89 | ) |
578 | 78 | from charmhelpers.contrib.openstack.utils import get_host_ip | 90 | from charmhelpers.contrib.openstack.utils import get_host_ip |
579 | 79 | |||
580 | 80 | CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' | 91 | CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' |
581 | 81 | ADDRESS_TYPES = ['admin', 'internal', 'public'] | 92 | ADDRESS_TYPES = ['admin', 'internal', 'public'] |
582 | 82 | 93 | ||
583 | @@ -108,9 +119,41 @@ | |||
584 | 108 | def config_flags_parser(config_flags): | 119 | def config_flags_parser(config_flags): |
585 | 109 | """Parses config flags string into dict. | 120 | """Parses config flags string into dict. |
586 | 110 | 121 | ||
587 | 122 | This parsing method supports a few different formats for the config | ||
588 | 123 | flag values to be parsed: | ||
589 | 124 | |||
590 | 125 | 1. A string in the simple format of key=value pairs, with the possibility | ||
591 | 126 | of specifying multiple key value pairs within the same string. For | ||
592 | 127 | example, a string in the format of 'key1=value1, key2=value2' will | ||
593 | 128 | return a dict of: | ||
594 | 129 | {'key1': 'value1', | ||
595 | 130 | 'key2': 'value2'}. | ||
596 | 131 | |||
597 | 132 | 2. A string in the above format, but supporting a comma-delimited list | ||
598 | 133 | of values for the same key. For example, a string in the format of | ||
599 | 134 | 'key1=value1, key2=value3,value4,value5' will return a dict of: | ||
600 | 135 | {'key1', 'value1', | ||
601 | 136 | 'key2', 'value2,value3,value4'} | ||
602 | 137 | |||
603 | 138 | 3. A string containing a colon character (:) prior to an equal | ||
604 | 139 | character (=) will be treated as yaml and parsed as such. This can be | ||
605 | 140 | used to specify more complex key value pairs. For example, | ||
606 | 141 | a string in the format of 'key1: subkey1=value1, subkey2=value2' will | ||
607 | 142 | return a dict of: | ||
608 | 143 | {'key1', 'subkey1=value1, subkey2=value2'} | ||
609 | 144 | |||
610 | 111 | The provided config_flags string may be a list of comma-separated values | 145 | The provided config_flags string may be a list of comma-separated values |
611 | 112 | which themselves may be comma-separated list of values. | 146 | which themselves may be comma-separated list of values. |
612 | 113 | """ | 147 | """ |
613 | 148 | # If we find a colon before an equals sign then treat it as yaml. | ||
614 | 149 | # Note: limit it to finding the colon first since this indicates assignment | ||
615 | 150 | # for inline yaml. | ||
616 | 151 | colon = config_flags.find(':') | ||
617 | 152 | equals = config_flags.find('=') | ||
618 | 153 | if colon > 0: | ||
619 | 154 | if colon < equals or equals < 0: | ||
620 | 155 | return yaml.safe_load(config_flags) | ||
621 | 156 | |||
622 | 114 | if config_flags.find('==') >= 0: | 157 | if config_flags.find('==') >= 0: |
623 | 115 | log("config_flags is not in expected format (key=value)", level=ERROR) | 158 | log("config_flags is not in expected format (key=value)", level=ERROR) |
624 | 116 | raise OSContextError | 159 | raise OSContextError |
625 | @@ -281,12 +324,29 @@ | |||
626 | 281 | 324 | ||
627 | 282 | 325 | ||
628 | 283 | class IdentityServiceContext(OSContextGenerator): | 326 | class IdentityServiceContext(OSContextGenerator): |
630 | 284 | interfaces = ['identity-service'] | 327 | |
631 | 328 | def __init__(self, service=None, service_user=None, rel_name='identity-service'): | ||
632 | 329 | self.service = service | ||
633 | 330 | self.service_user = service_user | ||
634 | 331 | self.rel_name = rel_name | ||
635 | 332 | self.interfaces = [self.rel_name] | ||
636 | 285 | 333 | ||
637 | 286 | def __call__(self): | 334 | def __call__(self): |
639 | 287 | log('Generating template context for identity-service', level=DEBUG) | 335 | log('Generating template context for ' + self.rel_name, level=DEBUG) |
640 | 288 | ctxt = {} | 336 | ctxt = {} |
642 | 289 | for rid in relation_ids('identity-service'): | 337 | |
643 | 338 | if self.service and self.service_user: | ||
644 | 339 | # This is required for pki token signing if we don't want /tmp to | ||
645 | 340 | # be used. | ||
646 | 341 | cachedir = '/var/cache/%s' % (self.service) | ||
647 | 342 | if not os.path.isdir(cachedir): | ||
648 | 343 | log("Creating service cache dir %s" % (cachedir), level=DEBUG) | ||
649 | 344 | mkdir(path=cachedir, owner=self.service_user, | ||
650 | 345 | group=self.service_user, perms=0o700) | ||
651 | 346 | |||
652 | 347 | ctxt['signing_dir'] = cachedir | ||
653 | 348 | |||
654 | 349 | for rid in relation_ids(self.rel_name): | ||
655 | 290 | for unit in related_units(rid): | 350 | for unit in related_units(rid): |
656 | 291 | rdata = relation_get(rid=rid, unit=unit) | 351 | rdata = relation_get(rid=rid, unit=unit) |
657 | 292 | serv_host = rdata.get('service_host') | 352 | serv_host = rdata.get('service_host') |
658 | @@ -295,15 +355,16 @@ | |||
659 | 295 | auth_host = format_ipv6_addr(auth_host) or auth_host | 355 | auth_host = format_ipv6_addr(auth_host) or auth_host |
660 | 296 | svc_protocol = rdata.get('service_protocol') or 'http' | 356 | svc_protocol = rdata.get('service_protocol') or 'http' |
661 | 297 | auth_protocol = rdata.get('auth_protocol') or 'http' | 357 | auth_protocol = rdata.get('auth_protocol') or 'http' |
671 | 298 | ctxt = {'service_port': rdata.get('service_port'), | 358 | ctxt.update({'service_port': rdata.get('service_port'), |
672 | 299 | 'service_host': serv_host, | 359 | 'service_host': serv_host, |
673 | 300 | 'auth_host': auth_host, | 360 | 'auth_host': auth_host, |
674 | 301 | 'auth_port': rdata.get('auth_port'), | 361 | 'auth_port': rdata.get('auth_port'), |
675 | 302 | 'admin_tenant_name': rdata.get('service_tenant'), | 362 | 'admin_tenant_name': rdata.get('service_tenant'), |
676 | 303 | 'admin_user': rdata.get('service_username'), | 363 | 'admin_user': rdata.get('service_username'), |
677 | 304 | 'admin_password': rdata.get('service_password'), | 364 | 'admin_password': rdata.get('service_password'), |
678 | 305 | 'service_protocol': svc_protocol, | 365 | 'service_protocol': svc_protocol, |
679 | 306 | 'auth_protocol': auth_protocol} | 366 | 'auth_protocol': auth_protocol}) |
680 | 367 | |||
681 | 307 | if context_complete(ctxt): | 368 | if context_complete(ctxt): |
682 | 308 | # NOTE(jamespage) this is required for >= icehouse | 369 | # NOTE(jamespage) this is required for >= icehouse |
683 | 309 | # so a missing value just indicates keystone needs | 370 | # so a missing value just indicates keystone needs |
684 | @@ -402,6 +463,11 @@ | |||
685 | 402 | 463 | ||
686 | 403 | ctxt['rabbitmq_hosts'] = ','.join(sorted(rabbitmq_hosts)) | 464 | ctxt['rabbitmq_hosts'] = ','.join(sorted(rabbitmq_hosts)) |
687 | 404 | 465 | ||
688 | 466 | oslo_messaging_flags = conf.get('oslo-messaging-flags', None) | ||
689 | 467 | if oslo_messaging_flags: | ||
690 | 468 | ctxt['oslo_messaging_flags'] = config_flags_parser( | ||
691 | 469 | oslo_messaging_flags) | ||
692 | 470 | |||
693 | 405 | if not context_complete(ctxt): | 471 | if not context_complete(ctxt): |
694 | 406 | return {} | 472 | return {} |
695 | 407 | 473 | ||
696 | @@ -751,6 +817,19 @@ | |||
697 | 751 | 817 | ||
698 | 752 | return ovs_ctxt | 818 | return ovs_ctxt |
699 | 753 | 819 | ||
700 | 820 | def nuage_ctxt(self): | ||
701 | 821 | driver = neutron_plugin_attribute(self.plugin, 'driver', | ||
702 | 822 | self.network_manager) | ||
703 | 823 | config = neutron_plugin_attribute(self.plugin, 'config', | ||
704 | 824 | self.network_manager) | ||
705 | 825 | nuage_ctxt = {'core_plugin': driver, | ||
706 | 826 | 'neutron_plugin': 'vsp', | ||
707 | 827 | 'neutron_security_groups': self.neutron_security_groups, | ||
708 | 828 | 'local_ip': unit_private_ip(), | ||
709 | 829 | 'config': config} | ||
710 | 830 | |||
711 | 831 | return nuage_ctxt | ||
712 | 832 | |||
713 | 754 | def nvp_ctxt(self): | 833 | def nvp_ctxt(self): |
714 | 755 | driver = neutron_plugin_attribute(self.plugin, 'driver', | 834 | driver = neutron_plugin_attribute(self.plugin, 'driver', |
715 | 756 | self.network_manager) | 835 | self.network_manager) |
716 | @@ -834,6 +913,8 @@ | |||
717 | 834 | ctxt.update(self.n1kv_ctxt()) | 913 | ctxt.update(self.n1kv_ctxt()) |
718 | 835 | elif self.plugin == 'Calico': | 914 | elif self.plugin == 'Calico': |
719 | 836 | ctxt.update(self.calico_ctxt()) | 915 | ctxt.update(self.calico_ctxt()) |
720 | 916 | elif self.plugin == 'vsp': | ||
721 | 917 | ctxt.update(self.nuage_ctxt()) | ||
722 | 837 | 918 | ||
723 | 838 | alchemy_flags = config('neutron-alchemy-flags') | 919 | alchemy_flags = config('neutron-alchemy-flags') |
724 | 839 | if alchemy_flags: | 920 | if alchemy_flags: |
725 | @@ -844,6 +925,48 @@ | |||
726 | 844 | return ctxt | 925 | return ctxt |
727 | 845 | 926 | ||
728 | 846 | 927 | ||
729 | 928 | class NeutronPortContext(OSContextGenerator): | ||
730 | 929 | NIC_PREFIXES = ['eth', 'bond'] | ||
731 | 930 | |||
732 | 931 | def resolve_ports(self, ports): | ||
733 | 932 | """Resolve NICs not yet bound to bridge(s) | ||
734 | 933 | |||
735 | 934 | If hwaddress provided then returns resolved hwaddress otherwise NIC. | ||
736 | 935 | """ | ||
737 | 936 | if not ports: | ||
738 | 937 | return None | ||
739 | 938 | |||
740 | 939 | hwaddr_to_nic = {} | ||
741 | 940 | hwaddr_to_ip = {} | ||
742 | 941 | for nic in list_nics(self.NIC_PREFIXES): | ||
743 | 942 | hwaddr = get_nic_hwaddr(nic) | ||
744 | 943 | hwaddr_to_nic[hwaddr] = nic | ||
745 | 944 | addresses = get_ipv4_addr(nic, fatal=False) | ||
746 | 945 | addresses += get_ipv6_addr(iface=nic, fatal=False) | ||
747 | 946 | hwaddr_to_ip[hwaddr] = addresses | ||
748 | 947 | |||
749 | 948 | resolved = [] | ||
750 | 949 | mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I) | ||
751 | 950 | for entry in ports: | ||
752 | 951 | if re.match(mac_regex, entry): | ||
753 | 952 | # NIC is in known NICs and does NOT hace an IP address | ||
754 | 953 | if entry in hwaddr_to_nic and not hwaddr_to_ip[entry]: | ||
755 | 954 | # If the nic is part of a bridge then don't use it | ||
756 | 955 | if is_bridge_member(hwaddr_to_nic[entry]): | ||
757 | 956 | continue | ||
758 | 957 | |||
759 | 958 | # Entry is a MAC address for a valid interface that doesn't | ||
760 | 959 | # have an IP address assigned yet. | ||
761 | 960 | resolved.append(hwaddr_to_nic[entry]) | ||
762 | 961 | else: | ||
763 | 962 | # If the passed entry is not a MAC address, assume it's a valid | ||
764 | 963 | # interface, and that the user put it there on purpose (we can | ||
765 | 964 | # trust it to be the real external network). | ||
766 | 965 | resolved.append(entry) | ||
767 | 966 | |||
768 | 967 | return resolved | ||
769 | 968 | |||
770 | 969 | |||
771 | 847 | class OSConfigFlagContext(OSContextGenerator): | 970 | class OSConfigFlagContext(OSContextGenerator): |
772 | 848 | """Provides support for user-defined config flags. | 971 | """Provides support for user-defined config flags. |
773 | 849 | 972 | ||
774 | @@ -1032,6 +1155,8 @@ | |||
775 | 1032 | for unit in related_units(rid): | 1155 | for unit in related_units(rid): |
776 | 1033 | ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) | 1156 | ctxt['zmq_nonce'] = relation_get('nonce', unit, rid) |
777 | 1034 | ctxt['zmq_host'] = relation_get('host', unit, rid) | 1157 | ctxt['zmq_host'] = relation_get('host', unit, rid) |
778 | 1158 | ctxt['zmq_redis_address'] = relation_get( | ||
779 | 1159 | 'zmq_redis_address', unit, rid) | ||
780 | 1035 | 1160 | ||
781 | 1036 | return ctxt | 1161 | return ctxt |
782 | 1037 | 1162 | ||
783 | @@ -1063,3 +1188,145 @@ | |||
784 | 1063 | sysctl_create(sysctl_dict, | 1188 | sysctl_create(sysctl_dict, |
785 | 1064 | '/etc/sysctl.d/50-{0}.conf'.format(charm_name())) | 1189 | '/etc/sysctl.d/50-{0}.conf'.format(charm_name())) |
786 | 1065 | return {'sysctl': sysctl_dict} | 1190 | return {'sysctl': sysctl_dict} |
787 | 1191 | |||
788 | 1192 | |||
789 | 1193 | class NeutronAPIContext(OSContextGenerator): | ||
790 | 1194 | ''' | ||
791 | 1195 | Inspects current neutron-plugin-api relation for neutron settings. Return | ||
792 | 1196 | defaults if it is not present. | ||
793 | 1197 | ''' | ||
794 | 1198 | interfaces = ['neutron-plugin-api'] | ||
795 | 1199 | |||
796 | 1200 | def __call__(self): | ||
797 | 1201 | self.neutron_defaults = { | ||
798 | 1202 | 'l2_population': { | ||
799 | 1203 | 'rel_key': 'l2-population', | ||
800 | 1204 | 'default': False, | ||
801 | 1205 | }, | ||
802 | 1206 | 'overlay_network_type': { | ||
803 | 1207 | 'rel_key': 'overlay-network-type', | ||
804 | 1208 | 'default': 'gre', | ||
805 | 1209 | }, | ||
806 | 1210 | 'neutron_security_groups': { | ||
807 | 1211 | 'rel_key': 'neutron-security-groups', | ||
808 | 1212 | 'default': False, | ||
809 | 1213 | }, | ||
810 | 1214 | 'network_device_mtu': { | ||
811 | 1215 | 'rel_key': 'network-device-mtu', | ||
812 | 1216 | 'default': None, | ||
813 | 1217 | }, | ||
814 | 1218 | 'enable_dvr': { | ||
815 | 1219 | 'rel_key': 'enable-dvr', | ||
816 | 1220 | 'default': False, | ||
817 | 1221 | }, | ||
818 | 1222 | 'enable_l3ha': { | ||
819 | 1223 | 'rel_key': 'enable-l3ha', | ||
820 | 1224 | 'default': False, | ||
821 | 1225 | }, | ||
822 | 1226 | } | ||
823 | 1227 | ctxt = self.get_neutron_options({}) | ||
824 | 1228 | for rid in relation_ids('neutron-plugin-api'): | ||
825 | 1229 | for unit in related_units(rid): | ||
826 | 1230 | rdata = relation_get(rid=rid, unit=unit) | ||
827 | 1231 | if 'l2-population' in rdata: | ||
828 | 1232 | ctxt.update(self.get_neutron_options(rdata)) | ||
829 | 1233 | |||
830 | 1234 | return ctxt | ||
831 | 1235 | |||
832 | 1236 | def get_neutron_options(self, rdata): | ||
833 | 1237 | settings = {} | ||
834 | 1238 | for nkey in self.neutron_defaults.keys(): | ||
835 | 1239 | defv = self.neutron_defaults[nkey]['default'] | ||
836 | 1240 | rkey = self.neutron_defaults[nkey]['rel_key'] | ||
837 | 1241 | if rkey in rdata.keys(): | ||
838 | 1242 | if type(defv) is bool: | ||
839 | 1243 | settings[nkey] = bool_from_string(rdata[rkey]) | ||
840 | 1244 | else: | ||
841 | 1245 | settings[nkey] = rdata[rkey] | ||
842 | 1246 | else: | ||
843 | 1247 | settings[nkey] = defv | ||
844 | 1248 | return settings | ||
845 | 1249 | |||
846 | 1250 | |||
847 | 1251 | class ExternalPortContext(NeutronPortContext): | ||
848 | 1252 | |||
849 | 1253 | def __call__(self): | ||
850 | 1254 | ctxt = {} | ||
851 | 1255 | ports = config('ext-port') | ||
852 | 1256 | if ports: | ||
853 | 1257 | ports = [p.strip() for p in ports.split()] | ||
854 | 1258 | ports = self.resolve_ports(ports) | ||
855 | 1259 | if ports: | ||
856 | 1260 | ctxt = {"ext_port": ports[0]} | ||
857 | 1261 | napi_settings = NeutronAPIContext()() | ||
858 | 1262 | mtu = napi_settings.get('network_device_mtu') | ||
859 | 1263 | if mtu: | ||
860 | 1264 | ctxt['ext_port_mtu'] = mtu | ||
861 | 1265 | |||
862 | 1266 | return ctxt | ||
863 | 1267 | |||
864 | 1268 | |||
865 | 1269 | class DataPortContext(NeutronPortContext): | ||
866 | 1270 | |||
867 | 1271 | def __call__(self): | ||
868 | 1272 | ports = config('data-port') | ||
869 | 1273 | if ports: | ||
870 | 1274 | portmap = parse_data_port_mappings(ports) | ||
871 | 1275 | ports = portmap.values() | ||
872 | 1276 | resolved = self.resolve_ports(ports) | ||
873 | 1277 | normalized = {get_nic_hwaddr(port): port for port in resolved | ||
874 | 1278 | if port not in ports} | ||
875 | 1279 | normalized.update({port: port for port in resolved | ||
876 | 1280 | if port in ports}) | ||
877 | 1281 | if resolved: | ||
878 | 1282 | return {bridge: normalized[port] for bridge, port in | ||
879 | 1283 | six.iteritems(portmap) if port in normalized.keys()} | ||
880 | 1284 | |||
881 | 1285 | return None | ||
882 | 1286 | |||
883 | 1287 | |||
884 | 1288 | class PhyNICMTUContext(DataPortContext): | ||
885 | 1289 | |||
886 | 1290 | def __call__(self): | ||
887 | 1291 | ctxt = {} | ||
888 | 1292 | mappings = super(PhyNICMTUContext, self).__call__() | ||
889 | 1293 | if mappings and mappings.values(): | ||
890 | 1294 | ports = mappings.values() | ||
891 | 1295 | napi_settings = NeutronAPIContext()() | ||
892 | 1296 | mtu = napi_settings.get('network_device_mtu') | ||
893 | 1297 | if mtu: | ||
894 | 1298 | ctxt["devs"] = '\\n'.join(ports) | ||
895 | 1299 | ctxt['mtu'] = mtu | ||
896 | 1300 | |||
897 | 1301 | return ctxt | ||
898 | 1302 | |||
899 | 1303 | |||
900 | 1304 | class NetworkServiceContext(OSContextGenerator): | ||
901 | 1305 | |||
902 | 1306 | def __init__(self, rel_name='quantum-network-service'): | ||
903 | 1307 | self.rel_name = rel_name | ||
904 | 1308 | self.interfaces = [rel_name] | ||
905 | 1309 | |||
906 | 1310 | def __call__(self): | ||
907 | 1311 | for rid in relation_ids(self.rel_name): | ||
908 | 1312 | for unit in related_units(rid): | ||
909 | 1313 | rdata = relation_get(rid=rid, unit=unit) | ||
910 | 1314 | ctxt = { | ||
911 | 1315 | 'keystone_host': rdata.get('keystone_host'), | ||
912 | 1316 | 'service_port': rdata.get('service_port'), | ||
913 | 1317 | 'auth_port': rdata.get('auth_port'), | ||
914 | 1318 | 'service_tenant': rdata.get('service_tenant'), | ||
915 | 1319 | 'service_username': rdata.get('service_username'), | ||
916 | 1320 | 'service_password': rdata.get('service_password'), | ||
917 | 1321 | 'quantum_host': rdata.get('quantum_host'), | ||
918 | 1322 | 'quantum_port': rdata.get('quantum_port'), | ||
919 | 1323 | 'quantum_url': rdata.get('quantum_url'), | ||
920 | 1324 | 'region': rdata.get('region'), | ||
921 | 1325 | 'service_protocol': | ||
922 | 1326 | rdata.get('service_protocol') or 'http', | ||
923 | 1327 | 'auth_protocol': | ||
924 | 1328 | rdata.get('auth_protocol') or 'http', | ||
925 | 1329 | } | ||
926 | 1330 | if context_complete(ctxt): | ||
927 | 1331 | return ctxt | ||
928 | 1332 | return {} | ||
929 | 1066 | 1333 | ||
930 | === added directory 'hooks/charmhelpers/contrib/openstack/files' | |||
931 | === added file 'hooks/charmhelpers/contrib/openstack/files/__init__.py' | |||
932 | --- hooks/charmhelpers/contrib/openstack/files/__init__.py 1970-01-01 00:00:00 +0000 | |||
933 | +++ hooks/charmhelpers/contrib/openstack/files/__init__.py 2015-04-16 21:50:07 +0000 | |||
934 | @@ -0,0 +1,18 @@ | |||
935 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
936 | 2 | # | ||
937 | 3 | # This file is part of charm-helpers. | ||
938 | 4 | # | ||
939 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
940 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
941 | 7 | # published by the Free Software Foundation. | ||
942 | 8 | # | ||
943 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
944 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
945 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
946 | 12 | # GNU Lesser General Public License for more details. | ||
947 | 13 | # | ||
948 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
949 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
950 | 16 | |||
951 | 17 | # dummy __init__.py to fool syncer into thinking this is a syncable python | ||
952 | 18 | # module | ||
953 | 0 | 19 | ||
954 | === added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh' | |||
955 | --- hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 1970-01-01 00:00:00 +0000 | |||
956 | +++ hooks/charmhelpers/contrib/openstack/files/check_haproxy.sh 2015-04-16 21:50:07 +0000 | |||
957 | @@ -0,0 +1,32 @@ | |||
958 | 1 | #!/bin/bash | ||
959 | 2 | #-------------------------------------------- | ||
960 | 3 | # This file is managed by Juju | ||
961 | 4 | #-------------------------------------------- | ||
962 | 5 | # | ||
963 | 6 | # Copyright 2009,2012 Canonical Ltd. | ||
964 | 7 | # Author: Tom Haddon | ||
965 | 8 | |||
966 | 9 | CRITICAL=0 | ||
967 | 10 | NOTACTIVE='' | ||
968 | 11 | LOGFILE=/var/log/nagios/check_haproxy.log | ||
969 | 12 | AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}') | ||
970 | 13 | |||
971 | 14 | for appserver in $(grep ' server' /etc/haproxy/haproxy.cfg | awk '{print $2'}); | ||
972 | 15 | do | ||
973 | 16 | output=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 --regex="class=\"(active|backup)(2|3).*${appserver}" -e ' 200 OK') | ||
974 | 17 | if [ $? != 0 ]; then | ||
975 | 18 | date >> $LOGFILE | ||
976 | 19 | echo $output >> $LOGFILE | ||
977 | 20 | /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -v | grep $appserver >> $LOGFILE 2>&1 | ||
978 | 21 | CRITICAL=1 | ||
979 | 22 | NOTACTIVE="${NOTACTIVE} $appserver" | ||
980 | 23 | fi | ||
981 | 24 | done | ||
982 | 25 | |||
983 | 26 | if [ $CRITICAL = 1 ]; then | ||
984 | 27 | echo "CRITICAL:${NOTACTIVE}" | ||
985 | 28 | exit 2 | ||
986 | 29 | fi | ||
987 | 30 | |||
988 | 31 | echo "OK: All haproxy instances looking good" | ||
989 | 32 | exit 0 | ||
990 | 0 | 33 | ||
991 | === added file 'hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh' | |||
992 | --- hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 1970-01-01 00:00:00 +0000 | |||
993 | +++ hooks/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh 2015-04-16 21:50:07 +0000 | |||
994 | @@ -0,0 +1,30 @@ | |||
995 | 1 | #!/bin/bash | ||
996 | 2 | #-------------------------------------------- | ||
997 | 3 | # This file is managed by Juju | ||
998 | 4 | #-------------------------------------------- | ||
999 | 5 | # | ||
1000 | 6 | # Copyright 2009,2012 Canonical Ltd. | ||
1001 | 7 | # Author: Tom Haddon | ||
1002 | 8 | |||
1003 | 9 | # These should be config options at some stage | ||
1004 | 10 | CURRQthrsh=0 | ||
1005 | 11 | MAXQthrsh=100 | ||
1006 | 12 | |||
1007 | 13 | AUTH=$(grep -r "stats auth" /etc/haproxy | head -1 | awk '{print $4}') | ||
1008 | 14 | |||
1009 | 15 | HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v) | ||
1010 | 16 | |||
1011 | 17 | for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}') | ||
1012 | 18 | do | ||
1013 | 19 | CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3) | ||
1014 | 20 | MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4) | ||
1015 | 21 | |||
1016 | 22 | if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then | ||
1017 | 23 | echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ" | ||
1018 | 24 | exit 2 | ||
1019 | 25 | fi | ||
1020 | 26 | done | ||
1021 | 27 | |||
1022 | 28 | echo "OK: All haproxy queue depths looking good" | ||
1023 | 29 | exit 0 | ||
1024 | 30 | |||
1025 | 0 | 31 | ||
1026 | === modified file 'hooks/charmhelpers/contrib/openstack/ip.py' | |||
1027 | --- hooks/charmhelpers/contrib/openstack/ip.py 2015-01-26 09:45:23 +0000 | |||
1028 | +++ hooks/charmhelpers/contrib/openstack/ip.py 2015-04-16 21:50:07 +0000 | |||
1029 | @@ -26,6 +26,8 @@ | |||
1030 | 26 | ) | 26 | ) |
1031 | 27 | from charmhelpers.contrib.hahelpers.cluster import is_clustered | 27 | from charmhelpers.contrib.hahelpers.cluster import is_clustered |
1032 | 28 | 28 | ||
1033 | 29 | from functools import partial | ||
1034 | 30 | |||
1035 | 29 | PUBLIC = 'public' | 31 | PUBLIC = 'public' |
1036 | 30 | INTERNAL = 'int' | 32 | INTERNAL = 'int' |
1037 | 31 | ADMIN = 'admin' | 33 | ADMIN = 'admin' |
1038 | @@ -107,3 +109,38 @@ | |||
1039 | 107 | "clustered=%s)" % (net_type, clustered)) | 109 | "clustered=%s)" % (net_type, clustered)) |
1040 | 108 | 110 | ||
1041 | 109 | return resolved_address | 111 | return resolved_address |
1042 | 112 | |||
1043 | 113 | |||
1044 | 114 | def endpoint_url(configs, url_template, port, endpoint_type=PUBLIC, | ||
1045 | 115 | override=None): | ||
1046 | 116 | """Returns the correct endpoint URL to advertise to Keystone. | ||
1047 | 117 | |||
1048 | 118 | This method provides the correct endpoint URL which should be advertised to | ||
1049 | 119 | the keystone charm for endpoint creation. This method allows for the url to | ||
1050 | 120 | be overridden to force a keystone endpoint to have specific URL for any of | ||
1051 | 121 | the defined scopes (admin, internal, public). | ||
1052 | 122 | |||
1053 | 123 | :param configs: OSTemplateRenderer config templating object to inspect | ||
1054 | 124 | for a complete https context. | ||
1055 | 125 | :param url_template: str format string for creating the url template. Only | ||
1056 | 126 | two values will be passed - the scheme+hostname | ||
1057 | 127 | returned by the canonical_url and the port. | ||
1058 | 128 | :param endpoint_type: str endpoint type to resolve. | ||
1059 | 129 | :param override: str the name of the config option which overrides the | ||
1060 | 130 | endpoint URL defined by the charm itself. None will | ||
1061 | 131 | disable any overrides (default). | ||
1062 | 132 | """ | ||
1063 | 133 | if override: | ||
1064 | 134 | # Return any user-defined overrides for the keystone endpoint URL. | ||
1065 | 135 | user_value = config(override) | ||
1066 | 136 | if user_value: | ||
1067 | 137 | return user_value.strip() | ||
1068 | 138 | |||
1069 | 139 | return url_template % (canonical_url(configs, endpoint_type), port) | ||
1070 | 140 | |||
1071 | 141 | |||
1072 | 142 | public_endpoint = partial(endpoint_url, endpoint_type=PUBLIC) | ||
1073 | 143 | |||
1074 | 144 | internal_endpoint = partial(endpoint_url, endpoint_type=INTERNAL) | ||
1075 | 145 | |||
1076 | 146 | admin_endpoint = partial(endpoint_url, endpoint_type=ADMIN) | ||
1077 | 110 | 147 | ||
1078 | === modified file 'hooks/charmhelpers/contrib/openstack/neutron.py' | |||
1079 | --- hooks/charmhelpers/contrib/openstack/neutron.py 2015-01-26 09:45:23 +0000 | |||
1080 | +++ hooks/charmhelpers/contrib/openstack/neutron.py 2015-04-16 21:50:07 +0000 | |||
1081 | @@ -16,6 +16,7 @@ | |||
1082 | 16 | 16 | ||
1083 | 17 | # Various utilies for dealing with Neutron and the renaming from Quantum. | 17 | # Various utilies for dealing with Neutron and the renaming from Quantum. |
1084 | 18 | 18 | ||
1085 | 19 | import six | ||
1086 | 19 | from subprocess import check_output | 20 | from subprocess import check_output |
1087 | 20 | 21 | ||
1088 | 21 | from charmhelpers.core.hookenv import ( | 22 | from charmhelpers.core.hookenv import ( |
1089 | @@ -179,6 +180,19 @@ | |||
1090 | 179 | 'nova-api-metadata']], | 180 | 'nova-api-metadata']], |
1091 | 180 | 'server_packages': ['neutron-server', 'calico-control'], | 181 | 'server_packages': ['neutron-server', 'calico-control'], |
1092 | 181 | 'server_services': ['neutron-server'] | 182 | 'server_services': ['neutron-server'] |
1093 | 183 | }, | ||
1094 | 184 | 'vsp': { | ||
1095 | 185 | 'config': '/etc/neutron/plugins/nuage/nuage_plugin.ini', | ||
1096 | 186 | 'driver': 'neutron.plugins.nuage.plugin.NuagePlugin', | ||
1097 | 187 | 'contexts': [ | ||
1098 | 188 | context.SharedDBContext(user=config('neutron-database-user'), | ||
1099 | 189 | database=config('neutron-database'), | ||
1100 | 190 | relation_prefix='neutron', | ||
1101 | 191 | ssl_dir=NEUTRON_CONF_DIR)], | ||
1102 | 192 | 'services': [], | ||
1103 | 193 | 'packages': [], | ||
1104 | 194 | 'server_packages': ['neutron-server', 'neutron-plugin-nuage'], | ||
1105 | 195 | 'server_services': ['neutron-server'] | ||
1106 | 182 | } | 196 | } |
1107 | 183 | } | 197 | } |
1108 | 184 | if release >= 'icehouse': | 198 | if release >= 'icehouse': |
1109 | @@ -237,3 +251,72 @@ | |||
1110 | 237 | else: | 251 | else: |
1111 | 238 | # ensure accurate naming for all releases post-H | 252 | # ensure accurate naming for all releases post-H |
1112 | 239 | return 'neutron' | 253 | return 'neutron' |
1113 | 254 | |||
1114 | 255 | |||
1115 | 256 | def parse_mappings(mappings): | ||
1116 | 257 | parsed = {} | ||
1117 | 258 | if mappings: | ||
1118 | 259 | mappings = mappings.split(' ') | ||
1119 | 260 | for m in mappings: | ||
1120 | 261 | p = m.partition(':') | ||
1121 | 262 | if p[1] == ':': | ||
1122 | 263 | parsed[p[0].strip()] = p[2].strip() | ||
1123 | 264 | |||
1124 | 265 | return parsed | ||
1125 | 266 | |||
1126 | 267 | |||
1127 | 268 | def parse_bridge_mappings(mappings): | ||
1128 | 269 | """Parse bridge mappings. | ||
1129 | 270 | |||
1130 | 271 | Mappings must be a space-delimited list of provider:bridge mappings. | ||
1131 | 272 | |||
1132 | 273 | Returns dict of the form {provider:bridge}. | ||
1133 | 274 | """ | ||
1134 | 275 | return parse_mappings(mappings) | ||
1135 | 276 | |||
1136 | 277 | |||
1137 | 278 | def parse_data_port_mappings(mappings, default_bridge='br-data'): | ||
1138 | 279 | """Parse data port mappings. | ||
1139 | 280 | |||
1140 | 281 | Mappings must be a space-delimited list of bridge:port mappings. | ||
1141 | 282 | |||
1142 | 283 | Returns dict of the form {bridge:port}. | ||
1143 | 284 | """ | ||
1144 | 285 | _mappings = parse_mappings(mappings) | ||
1145 | 286 | if not _mappings: | ||
1146 | 287 | if not mappings: | ||
1147 | 288 | return {} | ||
1148 | 289 | |||
1149 | 290 | # For backwards-compatibility we need to support port-only provided in | ||
1150 | 291 | # config. | ||
1151 | 292 | _mappings = {default_bridge: mappings.split(' ')[0]} | ||
1152 | 293 | |||
1153 | 294 | bridges = _mappings.keys() | ||
1154 | 295 | ports = _mappings.values() | ||
1155 | 296 | if len(set(bridges)) != len(bridges): | ||
1156 | 297 | raise Exception("It is not allowed to have more than one port " | ||
1157 | 298 | "configured on the same bridge") | ||
1158 | 299 | |||
1159 | 300 | if len(set(ports)) != len(ports): | ||
1160 | 301 | raise Exception("It is not allowed to have the same port configured " | ||
1161 | 302 | "on more than one bridge") | ||
1162 | 303 | |||
1163 | 304 | return _mappings | ||
1164 | 305 | |||
1165 | 306 | |||
1166 | 307 | def parse_vlan_range_mappings(mappings): | ||
1167 | 308 | """Parse vlan range mappings. | ||
1168 | 309 | |||
1169 | 310 | Mappings must be a space-delimited list of provider:start:end mappings. | ||
1170 | 311 | |||
1171 | 312 | Returns dict of the form {provider: (start, end)}. | ||
1172 | 313 | """ | ||
1173 | 314 | _mappings = parse_mappings(mappings) | ||
1174 | 315 | if not _mappings: | ||
1175 | 316 | return {} | ||
1176 | 317 | |||
1177 | 318 | mappings = {} | ||
1178 | 319 | for p, r in six.iteritems(_mappings): | ||
1179 | 320 | mappings[p] = tuple(r.split(':')) | ||
1180 | 321 | |||
1181 | 322 | return mappings | ||
1182 | 240 | 323 | ||
1183 | === added file 'hooks/charmhelpers/contrib/openstack/templates/git.upstart' | |||
1184 | --- hooks/charmhelpers/contrib/openstack/templates/git.upstart 1970-01-01 00:00:00 +0000 | |||
1185 | +++ hooks/charmhelpers/contrib/openstack/templates/git.upstart 2015-04-16 21:50:07 +0000 | |||
1186 | @@ -0,0 +1,17 @@ | |||
1187 | 1 | description "{{ service_description }}" | ||
1188 | 2 | author "Juju {{ service_name }} Charm <juju@localhost>" | ||
1189 | 3 | |||
1190 | 4 | start on runlevel [2345] | ||
1191 | 5 | stop on runlevel [!2345] | ||
1192 | 6 | |||
1193 | 7 | respawn | ||
1194 | 8 | |||
1195 | 9 | exec start-stop-daemon --start --chuid {{ user_name }} \ | ||
1196 | 10 | --chdir {{ start_dir }} --name {{ process_name }} \ | ||
1197 | 11 | --exec {{ executable_name }} -- \ | ||
1198 | 12 | {% for config_file in config_files -%} | ||
1199 | 13 | --config-file={{ config_file }} \ | ||
1200 | 14 | {% endfor -%} | ||
1201 | 15 | {% if log_file -%} | ||
1202 | 16 | --log-file={{ log_file }} | ||
1203 | 17 | {% endif -%} | ||
1204 | 0 | 18 | ||
1205 | === added file 'hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken' | |||
1206 | --- hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken 1970-01-01 00:00:00 +0000 | |||
1207 | +++ hooks/charmhelpers/contrib/openstack/templates/section-keystone-authtoken 2015-04-16 21:50:07 +0000 | |||
1208 | @@ -0,0 +1,9 @@ | |||
1209 | 1 | {% if auth_host -%} | ||
1210 | 2 | [keystone_authtoken] | ||
1211 | 3 | identity_uri = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/{{ auth_admin_prefix }} | ||
1212 | 4 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/{{ service_admin_prefix }} | ||
1213 | 5 | admin_tenant_name = {{ admin_tenant_name }} | ||
1214 | 6 | admin_user = {{ admin_user }} | ||
1215 | 7 | admin_password = {{ admin_password }} | ||
1216 | 8 | signing_dir = {{ signing_dir }} | ||
1217 | 9 | {% endif -%} | ||
1218 | 0 | 10 | ||
1219 | === added file 'hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo' | |||
1220 | --- hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo 1970-01-01 00:00:00 +0000 | |||
1221 | +++ hooks/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo 2015-04-16 21:50:07 +0000 | |||
1222 | @@ -0,0 +1,22 @@ | |||
1223 | 1 | {% if rabbitmq_host or rabbitmq_hosts -%} | ||
1224 | 2 | [oslo_messaging_rabbit] | ||
1225 | 3 | rabbit_userid = {{ rabbitmq_user }} | ||
1226 | 4 | rabbit_virtual_host = {{ rabbitmq_virtual_host }} | ||
1227 | 5 | rabbit_password = {{ rabbitmq_password }} | ||
1228 | 6 | {% if rabbitmq_hosts -%} | ||
1229 | 7 | rabbit_hosts = {{ rabbitmq_hosts }} | ||
1230 | 8 | {% if rabbitmq_ha_queues -%} | ||
1231 | 9 | rabbit_ha_queues = True | ||
1232 | 10 | rabbit_durable_queues = False | ||
1233 | 11 | {% endif -%} | ||
1234 | 12 | {% else -%} | ||
1235 | 13 | rabbit_host = {{ rabbitmq_host }} | ||
1236 | 14 | {% endif -%} | ||
1237 | 15 | {% if rabbit_ssl_port -%} | ||
1238 | 16 | rabbit_use_ssl = True | ||
1239 | 17 | rabbit_port = {{ rabbit_ssl_port }} | ||
1240 | 18 | {% if rabbit_ssl_ca -%} | ||
1241 | 19 | kombu_ssl_ca_certs = {{ rabbit_ssl_ca }} | ||
1242 | 20 | {% endif -%} | ||
1243 | 21 | {% endif -%} | ||
1244 | 22 | {% endif -%} | ||
1245 | 0 | 23 | ||
1246 | === added file 'hooks/charmhelpers/contrib/openstack/templates/section-zeromq' | |||
1247 | --- hooks/charmhelpers/contrib/openstack/templates/section-zeromq 1970-01-01 00:00:00 +0000 | |||
1248 | +++ hooks/charmhelpers/contrib/openstack/templates/section-zeromq 2015-04-16 21:50:07 +0000 | |||
1249 | @@ -0,0 +1,14 @@ | |||
1250 | 1 | {% if zmq_host -%} | ||
1251 | 2 | # ZeroMQ configuration (restart-nonce: {{ zmq_nonce }}) | ||
1252 | 3 | rpc_backend = zmq | ||
1253 | 4 | rpc_zmq_host = {{ zmq_host }} | ||
1254 | 5 | {% if zmq_redis_address -%} | ||
1255 | 6 | rpc_zmq_matchmaker = redis | ||
1256 | 7 | matchmaker_heartbeat_freq = 15 | ||
1257 | 8 | matchmaker_heartbeat_ttl = 30 | ||
1258 | 9 | [matchmaker_redis] | ||
1259 | 10 | host = {{ zmq_redis_address }} | ||
1260 | 11 | {% else -%} | ||
1261 | 12 | rpc_zmq_matchmaker = ring | ||
1262 | 13 | {% endif -%} | ||
1263 | 14 | {% endif -%} | ||
1264 | 0 | 15 | ||
1265 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
1266 | --- hooks/charmhelpers/contrib/openstack/utils.py 2015-01-26 09:45:23 +0000 | |||
1267 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2015-04-16 21:50:07 +0000 | |||
1268 | @@ -23,12 +23,17 @@ | |||
1269 | 23 | import subprocess | 23 | import subprocess |
1270 | 24 | import json | 24 | import json |
1271 | 25 | import os | 25 | import os |
1272 | 26 | import socket | ||
1273 | 27 | import sys | 26 | import sys |
1274 | 28 | 27 | ||
1275 | 29 | import six | 28 | import six |
1276 | 30 | import yaml | 29 | import yaml |
1277 | 31 | 30 | ||
1278 | 31 | from charmhelpers.contrib.network import ip | ||
1279 | 32 | |||
1280 | 33 | from charmhelpers.core import ( | ||
1281 | 34 | unitdata, | ||
1282 | 35 | ) | ||
1283 | 36 | |||
1284 | 32 | from charmhelpers.core.hookenv import ( | 37 | from charmhelpers.core.hookenv import ( |
1285 | 33 | config, | 38 | config, |
1286 | 34 | log as juju_log, | 39 | log as juju_log, |
1287 | @@ -103,6 +108,7 @@ | |||
1288 | 103 | ('2.1.0', 'juno'), | 108 | ('2.1.0', 'juno'), |
1289 | 104 | ('2.2.0', 'juno'), | 109 | ('2.2.0', 'juno'), |
1290 | 105 | ('2.2.1', 'kilo'), | 110 | ('2.2.1', 'kilo'), |
1291 | 111 | ('2.2.2', 'kilo'), | ||
1292 | 106 | ]) | 112 | ]) |
1293 | 107 | 113 | ||
1294 | 108 | DEFAULT_LOOPBACK_SIZE = '5G' | 114 | DEFAULT_LOOPBACK_SIZE = '5G' |
1295 | @@ -328,6 +334,21 @@ | |||
1296 | 328 | error_out("Invalid openstack-release specified: %s" % rel) | 334 | error_out("Invalid openstack-release specified: %s" % rel) |
1297 | 329 | 335 | ||
1298 | 330 | 336 | ||
1299 | 337 | def config_value_changed(option): | ||
1300 | 338 | """ | ||
1301 | 339 | Determine if config value changed since last call to this function. | ||
1302 | 340 | """ | ||
1303 | 341 | hook_data = unitdata.HookData() | ||
1304 | 342 | with hook_data(): | ||
1305 | 343 | db = unitdata.kv() | ||
1306 | 344 | current = config(option) | ||
1307 | 345 | saved = db.get(option) | ||
1308 | 346 | db.set(option, current) | ||
1309 | 347 | if saved is None: | ||
1310 | 348 | return False | ||
1311 | 349 | return current != saved | ||
1312 | 350 | |||
1313 | 351 | |||
1314 | 331 | def save_script_rc(script_path="scripts/scriptrc", **env_vars): | 352 | def save_script_rc(script_path="scripts/scriptrc", **env_vars): |
1315 | 332 | """ | 353 | """ |
1316 | 333 | Write an rc file in the charm-delivered directory containing | 354 | Write an rc file in the charm-delivered directory containing |
1317 | @@ -420,77 +441,10 @@ | |||
1318 | 420 | else: | 441 | else: |
1319 | 421 | zap_disk(block_device) | 442 | zap_disk(block_device) |
1320 | 422 | 443 | ||
1392 | 423 | 444 | is_ip = ip.is_ip | |
1393 | 424 | def is_ip(address): | 445 | ns_query = ip.ns_query |
1394 | 425 | """ | 446 | get_host_ip = ip.get_host_ip |
1395 | 426 | Returns True if address is a valid IP address. | 447 | get_hostname = ip.get_hostname |
1325 | 427 | """ | ||
1326 | 428 | try: | ||
1327 | 429 | # Test to see if already an IPv4 address | ||
1328 | 430 | socket.inet_aton(address) | ||
1329 | 431 | return True | ||
1330 | 432 | except socket.error: | ||
1331 | 433 | return False | ||
1332 | 434 | |||
1333 | 435 | |||
1334 | 436 | def ns_query(address): | ||
1335 | 437 | try: | ||
1336 | 438 | import dns.resolver | ||
1337 | 439 | except ImportError: | ||
1338 | 440 | apt_install('python-dnspython') | ||
1339 | 441 | import dns.resolver | ||
1340 | 442 | |||
1341 | 443 | if isinstance(address, dns.name.Name): | ||
1342 | 444 | rtype = 'PTR' | ||
1343 | 445 | elif isinstance(address, six.string_types): | ||
1344 | 446 | rtype = 'A' | ||
1345 | 447 | else: | ||
1346 | 448 | return None | ||
1347 | 449 | |||
1348 | 450 | answers = dns.resolver.query(address, rtype) | ||
1349 | 451 | if answers: | ||
1350 | 452 | return str(answers[0]) | ||
1351 | 453 | return None | ||
1352 | 454 | |||
1353 | 455 | |||
1354 | 456 | def get_host_ip(hostname): | ||
1355 | 457 | """ | ||
1356 | 458 | Resolves the IP for a given hostname, or returns | ||
1357 | 459 | the input if it is already an IP. | ||
1358 | 460 | """ | ||
1359 | 461 | if is_ip(hostname): | ||
1360 | 462 | return hostname | ||
1361 | 463 | |||
1362 | 464 | return ns_query(hostname) | ||
1363 | 465 | |||
1364 | 466 | |||
1365 | 467 | def get_hostname(address, fqdn=True): | ||
1366 | 468 | """ | ||
1367 | 469 | Resolves hostname for given IP, or returns the input | ||
1368 | 470 | if it is already a hostname. | ||
1369 | 471 | """ | ||
1370 | 472 | if is_ip(address): | ||
1371 | 473 | try: | ||
1372 | 474 | import dns.reversename | ||
1373 | 475 | except ImportError: | ||
1374 | 476 | apt_install('python-dnspython') | ||
1375 | 477 | import dns.reversename | ||
1376 | 478 | |||
1377 | 479 | rev = dns.reversename.from_address(address) | ||
1378 | 480 | result = ns_query(rev) | ||
1379 | 481 | if not result: | ||
1380 | 482 | return None | ||
1381 | 483 | else: | ||
1382 | 484 | result = address | ||
1383 | 485 | |||
1384 | 486 | if fqdn: | ||
1385 | 487 | # strip trailing . | ||
1386 | 488 | if result.endswith('.'): | ||
1387 | 489 | return result[:-1] | ||
1388 | 490 | else: | ||
1389 | 491 | return result | ||
1390 | 492 | else: | ||
1391 | 493 | return result.split('.')[0] | ||
1396 | 494 | 448 | ||
1397 | 495 | 449 | ||
1398 | 496 | def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'): | 450 | def get_matchmaker_map(mm_file='/etc/oslo/matchmaker_ring.json'): |
1399 | @@ -534,82 +488,106 @@ | |||
1400 | 534 | 488 | ||
1401 | 535 | 489 | ||
1402 | 536 | def git_install_requested(): | 490 | def git_install_requested(): |
1405 | 537 | """Returns true if openstack-origin-git is specified.""" | 491 | """ |
1406 | 538 | return config('openstack-origin-git') != "None" | 492 | Returns true if openstack-origin-git is specified. |
1407 | 493 | """ | ||
1408 | 494 | return config('openstack-origin-git') is not None | ||
1409 | 539 | 495 | ||
1410 | 540 | 496 | ||
1411 | 541 | requirements_dir = None | 497 | requirements_dir = None |
1412 | 542 | 498 | ||
1413 | 543 | 499 | ||
1416 | 544 | def git_clone_and_install(file_name, core_project): | 500 | def git_clone_and_install(projects_yaml, core_project): |
1417 | 545 | """Clone/install all OpenStack repos specified in yaml config file.""" | 501 | """ |
1418 | 502 | Clone/install all specified OpenStack repositories. | ||
1419 | 503 | |||
1420 | 504 | The expected format of projects_yaml is: | ||
1421 | 505 | repositories: | ||
1422 | 506 | - {name: keystone, | ||
1423 | 507 | repository: 'git://git.openstack.org/openstack/keystone.git', | ||
1424 | 508 | branch: 'stable/icehouse'} | ||
1425 | 509 | - {name: requirements, | ||
1426 | 510 | repository: 'git://git.openstack.org/openstack/requirements.git', | ||
1427 | 511 | branch: 'stable/icehouse'} | ||
1428 | 512 | directory: /mnt/openstack-git | ||
1429 | 513 | http_proxy: http://squid.internal:3128 | ||
1430 | 514 | https_proxy: https://squid.internal:3128 | ||
1431 | 515 | |||
1432 | 516 | The directory, http_proxy, and https_proxy keys are optional. | ||
1433 | 517 | """ | ||
1434 | 546 | global requirements_dir | 518 | global requirements_dir |
1435 | 519 | parent_dir = '/mnt/openstack-git' | ||
1436 | 547 | 520 | ||
1438 | 548 | if file_name == "None": | 521 | if not projects_yaml: |
1439 | 549 | return | 522 | return |
1440 | 550 | 523 | ||
1499 | 551 | yaml_file = os.path.join(charm_dir(), file_name) | 524 | projects = yaml.load(projects_yaml) |
1500 | 552 | 525 | _git_validate_projects_yaml(projects, core_project) | |
1501 | 553 | # clone/install the requirements project first | 526 | |
1502 | 554 | installed = _git_clone_and_install_subset(yaml_file, | 527 | old_environ = dict(os.environ) |
1503 | 555 | whitelist=['requirements']) | 528 | |
1504 | 556 | if 'requirements' not in installed: | 529 | if 'http_proxy' in projects.keys(): |
1505 | 557 | error_out('requirements git repository must be specified') | 530 | os.environ['http_proxy'] = projects['http_proxy'] |
1506 | 558 | 531 | if 'https_proxy' in projects.keys(): | |
1507 | 559 | # clone/install all other projects except requirements and the core project | 532 | os.environ['https_proxy'] = projects['https_proxy'] |
1508 | 560 | blacklist = ['requirements', core_project] | 533 | |
1509 | 561 | _git_clone_and_install_subset(yaml_file, blacklist=blacklist, | 534 | if 'directory' in projects.keys(): |
1510 | 562 | update_requirements=True) | 535 | parent_dir = projects['directory'] |
1511 | 563 | 536 | ||
1512 | 564 | # clone/install the core project | 537 | for p in projects['repositories']: |
1513 | 565 | whitelist = [core_project] | 538 | repo = p['repository'] |
1514 | 566 | installed = _git_clone_and_install_subset(yaml_file, whitelist=whitelist, | 539 | branch = p['branch'] |
1515 | 567 | update_requirements=True) | 540 | if p['name'] == 'requirements': |
1516 | 568 | if core_project not in installed: | 541 | repo_dir = _git_clone_and_install_single(repo, branch, parent_dir, |
1517 | 569 | error_out('{} git repository must be specified'.format(core_project)) | 542 | update_requirements=False) |
1518 | 570 | 543 | requirements_dir = repo_dir | |
1519 | 571 | 544 | else: | |
1520 | 572 | def _git_clone_and_install_subset(yaml_file, whitelist=[], blacklist=[], | 545 | repo_dir = _git_clone_and_install_single(repo, branch, parent_dir, |
1521 | 573 | update_requirements=False): | 546 | update_requirements=True) |
1522 | 574 | """Clone/install subset of OpenStack repos specified in yaml config file.""" | 547 | |
1523 | 575 | global requirements_dir | 548 | os.environ = old_environ |
1524 | 576 | installed = [] | 549 | |
1525 | 577 | 550 | ||
1526 | 578 | with open(yaml_file, 'r') as fd: | 551 | def _git_validate_projects_yaml(projects, core_project): |
1527 | 579 | projects = yaml.load(fd) | 552 | """ |
1528 | 580 | for proj, val in projects.items(): | 553 | Validate the projects yaml. |
1529 | 581 | # The project subset is chosen based on the following 3 rules: | 554 | """ |
1530 | 582 | # 1) If project is in blacklist, we don't clone/install it, period. | 555 | _git_ensure_key_exists('repositories', projects) |
1531 | 583 | # 2) If whitelist is empty, we clone/install everything else. | 556 | |
1532 | 584 | # 3) If whitelist is not empty, we clone/install everything in the | 557 | for project in projects['repositories']: |
1533 | 585 | # whitelist. | 558 | _git_ensure_key_exists('name', project.keys()) |
1534 | 586 | if proj in blacklist: | 559 | _git_ensure_key_exists('repository', project.keys()) |
1535 | 587 | continue | 560 | _git_ensure_key_exists('branch', project.keys()) |
1536 | 588 | if whitelist and proj not in whitelist: | 561 | |
1537 | 589 | continue | 562 | if projects['repositories'][0]['name'] != 'requirements': |
1538 | 590 | repo = val['repository'] | 563 | error_out('{} git repo must be specified first'.format('requirements')) |
1539 | 591 | branch = val['branch'] | 564 | |
1540 | 592 | repo_dir = _git_clone_and_install_single(repo, branch, | 565 | if projects['repositories'][-1]['name'] != core_project: |
1541 | 593 | update_requirements) | 566 | error_out('{} git repo must be specified last'.format(core_project)) |
1542 | 594 | if proj == 'requirements': | 567 | |
1543 | 595 | requirements_dir = repo_dir | 568 | |
1544 | 596 | installed.append(proj) | 569 | def _git_ensure_key_exists(key, keys): |
1545 | 597 | return installed | 570 | """ |
1546 | 598 | 571 | Ensure that key exists in keys. | |
1547 | 599 | 572 | """ | |
1548 | 600 | def _git_clone_and_install_single(repo, branch, update_requirements=False): | 573 | if key not in keys: |
1549 | 601 | """Clone and install a single git repository.""" | 574 | error_out('openstack-origin-git key \'{}\' is missing'.format(key)) |
1550 | 602 | dest_parent_dir = "/mnt/openstack-git/" | 575 | |
1551 | 603 | dest_dir = os.path.join(dest_parent_dir, os.path.basename(repo)) | 576 | |
1552 | 604 | 577 | def _git_clone_and_install_single(repo, branch, parent_dir, update_requirements): | |
1553 | 605 | if not os.path.exists(dest_parent_dir): | 578 | """ |
1554 | 606 | juju_log('Host dir not mounted at {}. ' | 579 | Clone and install a single git repository. |
1555 | 607 | 'Creating directory there instead.'.format(dest_parent_dir)) | 580 | """ |
1556 | 608 | os.mkdir(dest_parent_dir) | 581 | dest_dir = os.path.join(parent_dir, os.path.basename(repo)) |
1557 | 582 | |||
1558 | 583 | if not os.path.exists(parent_dir): | ||
1559 | 584 | juju_log('Directory already exists at {}. ' | ||
1560 | 585 | 'No need to create directory.'.format(parent_dir)) | ||
1561 | 586 | os.mkdir(parent_dir) | ||
1562 | 609 | 587 | ||
1563 | 610 | if not os.path.exists(dest_dir): | 588 | if not os.path.exists(dest_dir): |
1564 | 611 | juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch)) | 589 | juju_log('Cloning git repo: {}, branch: {}'.format(repo, branch)) |
1566 | 612 | repo_dir = install_remote(repo, dest=dest_parent_dir, branch=branch) | 590 | repo_dir = install_remote(repo, dest=parent_dir, branch=branch) |
1567 | 613 | else: | 591 | else: |
1568 | 614 | repo_dir = dest_dir | 592 | repo_dir = dest_dir |
1569 | 615 | 593 | ||
1570 | @@ -626,16 +604,39 @@ | |||
1571 | 626 | 604 | ||
1572 | 627 | 605 | ||
1573 | 628 | def _git_update_requirements(package_dir, reqs_dir): | 606 | def _git_update_requirements(package_dir, reqs_dir): |
1575 | 629 | """Update from global requirements. | 607 | """ |
1576 | 608 | Update from global requirements. | ||
1577 | 630 | 609 | ||
1580 | 631 | Update an OpenStack git directory's requirements.txt and | 610 | Update an OpenStack git directory's requirements.txt and |
1581 | 632 | test-requirements.txt from global-requirements.txt.""" | 611 | test-requirements.txt from global-requirements.txt. |
1582 | 612 | """ | ||
1583 | 633 | orig_dir = os.getcwd() | 613 | orig_dir = os.getcwd() |
1584 | 634 | os.chdir(reqs_dir) | 614 | os.chdir(reqs_dir) |
1586 | 635 | cmd = "python update.py {}".format(package_dir) | 615 | cmd = ['python', 'update.py', package_dir] |
1587 | 636 | try: | 616 | try: |
1589 | 637 | subprocess.check_call(cmd.split(' ')) | 617 | subprocess.check_call(cmd) |
1590 | 638 | except subprocess.CalledProcessError: | 618 | except subprocess.CalledProcessError: |
1591 | 639 | package = os.path.basename(package_dir) | 619 | package = os.path.basename(package_dir) |
1592 | 640 | error_out("Error updating {} from global-requirements.txt".format(package)) | 620 | error_out("Error updating {} from global-requirements.txt".format(package)) |
1593 | 641 | os.chdir(orig_dir) | 621 | os.chdir(orig_dir) |
1594 | 622 | |||
1595 | 623 | |||
1596 | 624 | def git_src_dir(projects_yaml, project): | ||
1597 | 625 | """ | ||
1598 | 626 | Return the directory where the specified project's source is located. | ||
1599 | 627 | """ | ||
1600 | 628 | parent_dir = '/mnt/openstack-git' | ||
1601 | 629 | |||
1602 | 630 | if not projects_yaml: | ||
1603 | 631 | return | ||
1604 | 632 | |||
1605 | 633 | projects = yaml.load(projects_yaml) | ||
1606 | 634 | |||
1607 | 635 | if 'directory' in projects.keys(): | ||
1608 | 636 | parent_dir = projects['directory'] | ||
1609 | 637 | |||
1610 | 638 | for p in projects['repositories']: | ||
1611 | 639 | if p['name'] == project: | ||
1612 | 640 | return os.path.join(parent_dir, os.path.basename(p['repository'])) | ||
1613 | 641 | |||
1614 | 642 | return None | ||
1615 | 642 | 643 | ||
1616 | === modified file 'hooks/charmhelpers/contrib/python/packages.py' | |||
1617 | --- hooks/charmhelpers/contrib/python/packages.py 2015-01-26 09:45:23 +0000 | |||
1618 | +++ hooks/charmhelpers/contrib/python/packages.py 2015-04-16 21:50:07 +0000 | |||
1619 | @@ -17,8 +17,6 @@ | |||
1620 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
1621 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
1622 | 19 | 19 | ||
1623 | 20 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
1624 | 21 | |||
1625 | 22 | from charmhelpers.fetch import apt_install, apt_update | 20 | from charmhelpers.fetch import apt_install, apt_update |
1626 | 23 | from charmhelpers.core.hookenv import log | 21 | from charmhelpers.core.hookenv import log |
1627 | 24 | 22 | ||
1628 | @@ -29,6 +27,8 @@ | |||
1629 | 29 | apt_install('python-pip') | 27 | apt_install('python-pip') |
1630 | 30 | from pip import main as pip_execute | 28 | from pip import main as pip_execute |
1631 | 31 | 29 | ||
1632 | 30 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
1633 | 31 | |||
1634 | 32 | 32 | ||
1635 | 33 | def parse_options(given, available): | 33 | def parse_options(given, available): |
1636 | 34 | """Given a set of options, check if available""" | 34 | """Given a set of options, check if available""" |
1637 | 35 | 35 | ||
1638 | === modified file 'hooks/charmhelpers/core/fstab.py' | |||
1639 | --- hooks/charmhelpers/core/fstab.py 2015-01-26 09:45:23 +0000 | |||
1640 | +++ hooks/charmhelpers/core/fstab.py 2015-04-16 21:50:07 +0000 | |||
1641 | @@ -17,11 +17,11 @@ | |||
1642 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
1643 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
1644 | 19 | 19 | ||
1645 | 20 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
1646 | 21 | |||
1647 | 22 | import io | 20 | import io |
1648 | 23 | import os | 21 | import os |
1649 | 24 | 22 | ||
1650 | 23 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
1651 | 24 | |||
1652 | 25 | 25 | ||
1653 | 26 | class Fstab(io.FileIO): | 26 | class Fstab(io.FileIO): |
1654 | 27 | """This class extends file in order to implement a file reader/writer | 27 | """This class extends file in order to implement a file reader/writer |
1655 | @@ -77,7 +77,7 @@ | |||
1656 | 77 | for line in self.readlines(): | 77 | for line in self.readlines(): |
1657 | 78 | line = line.decode('us-ascii') | 78 | line = line.decode('us-ascii') |
1658 | 79 | try: | 79 | try: |
1660 | 80 | if line.strip() and not line.startswith("#"): | 80 | if line.strip() and not line.strip().startswith("#"): |
1661 | 81 | yield self._hydrate_entry(line) | 81 | yield self._hydrate_entry(line) |
1662 | 82 | except ValueError: | 82 | except ValueError: |
1663 | 83 | pass | 83 | pass |
1664 | @@ -104,7 +104,7 @@ | |||
1665 | 104 | 104 | ||
1666 | 105 | found = False | 105 | found = False |
1667 | 106 | for index, line in enumerate(lines): | 106 | for index, line in enumerate(lines): |
1669 | 107 | if not line.startswith("#"): | 107 | if line.strip() and not line.strip().startswith("#"): |
1670 | 108 | if self._hydrate_entry(line) == entry: | 108 | if self._hydrate_entry(line) == entry: |
1671 | 109 | found = True | 109 | found = True |
1672 | 110 | break | 110 | break |
1673 | 111 | 111 | ||
1674 | === modified file 'hooks/charmhelpers/core/hookenv.py' | |||
1675 | --- hooks/charmhelpers/core/hookenv.py 2015-01-26 09:45:23 +0000 | |||
1676 | +++ hooks/charmhelpers/core/hookenv.py 2015-04-16 21:50:07 +0000 | |||
1677 | @@ -20,11 +20,13 @@ | |||
1678 | 20 | # Authors: | 20 | # Authors: |
1679 | 21 | # Charm Helpers Developers <juju@lists.ubuntu.com> | 21 | # Charm Helpers Developers <juju@lists.ubuntu.com> |
1680 | 22 | 22 | ||
1681 | 23 | from __future__ import print_function | ||
1682 | 23 | import os | 24 | import os |
1683 | 24 | import json | 25 | import json |
1684 | 25 | import yaml | 26 | import yaml |
1685 | 26 | import subprocess | 27 | import subprocess |
1686 | 27 | import sys | 28 | import sys |
1687 | 29 | import errno | ||
1688 | 28 | from subprocess import CalledProcessError | 30 | from subprocess import CalledProcessError |
1689 | 29 | 31 | ||
1690 | 30 | import six | 32 | import six |
1691 | @@ -87,7 +89,18 @@ | |||
1692 | 87 | if not isinstance(message, six.string_types): | 89 | if not isinstance(message, six.string_types): |
1693 | 88 | message = repr(message) | 90 | message = repr(message) |
1694 | 89 | command += [message] | 91 | command += [message] |
1696 | 90 | subprocess.call(command) | 92 | # Missing juju-log should not cause failures in unit tests |
1697 | 93 | # Send log output to stderr | ||
1698 | 94 | try: | ||
1699 | 95 | subprocess.call(command) | ||
1700 | 96 | except OSError as e: | ||
1701 | 97 | if e.errno == errno.ENOENT: | ||
1702 | 98 | if level: | ||
1703 | 99 | message = "{}: {}".format(level, message) | ||
1704 | 100 | message = "juju-log: {}".format(message) | ||
1705 | 101 | print(message, file=sys.stderr) | ||
1706 | 102 | else: | ||
1707 | 103 | raise | ||
1708 | 91 | 104 | ||
1709 | 92 | 105 | ||
1710 | 93 | class Serializable(UserDict): | 106 | class Serializable(UserDict): |
1711 | @@ -566,3 +579,29 @@ | |||
1712 | 566 | def charm_dir(): | 579 | def charm_dir(): |
1713 | 567 | """Return the root directory of the current charm""" | 580 | """Return the root directory of the current charm""" |
1714 | 568 | return os.environ.get('CHARM_DIR') | 581 | return os.environ.get('CHARM_DIR') |
1715 | 582 | |||
1716 | 583 | |||
1717 | 584 | @cached | ||
1718 | 585 | def action_get(key=None): | ||
1719 | 586 | """Gets the value of an action parameter, or all key/value param pairs""" | ||
1720 | 587 | cmd = ['action-get'] | ||
1721 | 588 | if key is not None: | ||
1722 | 589 | cmd.append(key) | ||
1723 | 590 | cmd.append('--format=json') | ||
1724 | 591 | action_data = json.loads(subprocess.check_output(cmd).decode('UTF-8')) | ||
1725 | 592 | return action_data | ||
1726 | 593 | |||
1727 | 594 | |||
1728 | 595 | def action_set(values): | ||
1729 | 596 | """Sets the values to be returned after the action finishes""" | ||
1730 | 597 | cmd = ['action-set'] | ||
1731 | 598 | for k, v in list(values.items()): | ||
1732 | 599 | cmd.append('{}={}'.format(k, v)) | ||
1733 | 600 | subprocess.check_call(cmd) | ||
1734 | 601 | |||
1735 | 602 | |||
1736 | 603 | def action_fail(message): | ||
1737 | 604 | """Sets the action status to failed and sets the error message. | ||
1738 | 605 | |||
1739 | 606 | The results set by action_set are preserved.""" | ||
1740 | 607 | subprocess.check_call(['action-fail', message]) | ||
1741 | 569 | 608 | ||
1742 | === modified file 'hooks/charmhelpers/core/host.py' | |||
1743 | --- hooks/charmhelpers/core/host.py 2015-01-26 09:45:23 +0000 | |||
1744 | +++ hooks/charmhelpers/core/host.py 2015-04-16 21:50:07 +0000 | |||
1745 | @@ -191,11 +191,11 @@ | |||
1746 | 191 | 191 | ||
1747 | 192 | 192 | ||
1748 | 193 | def write_file(path, content, owner='root', group='root', perms=0o444): | 193 | def write_file(path, content, owner='root', group='root', perms=0o444): |
1750 | 194 | """Create or overwrite a file with the contents of a string""" | 194 | """Create or overwrite a file with the contents of a byte string.""" |
1751 | 195 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) | 195 | log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) |
1752 | 196 | uid = pwd.getpwnam(owner).pw_uid | 196 | uid = pwd.getpwnam(owner).pw_uid |
1753 | 197 | gid = grp.getgrnam(group).gr_gid | 197 | gid = grp.getgrnam(group).gr_gid |
1755 | 198 | with open(path, 'w') as target: | 198 | with open(path, 'wb') as target: |
1756 | 199 | os.fchown(target.fileno(), uid, gid) | 199 | os.fchown(target.fileno(), uid, gid) |
1757 | 200 | os.fchmod(target.fileno(), perms) | 200 | os.fchmod(target.fileno(), perms) |
1758 | 201 | target.write(content) | 201 | target.write(content) |
1759 | @@ -305,11 +305,11 @@ | |||
1760 | 305 | ceph_client_changed function. | 305 | ceph_client_changed function. |
1761 | 306 | """ | 306 | """ |
1762 | 307 | def wrap(f): | 307 | def wrap(f): |
1764 | 308 | def wrapped_f(*args): | 308 | def wrapped_f(*args, **kwargs): |
1765 | 309 | checksums = {} | 309 | checksums = {} |
1766 | 310 | for path in restart_map: | 310 | for path in restart_map: |
1767 | 311 | checksums[path] = file_hash(path) | 311 | checksums[path] = file_hash(path) |
1769 | 312 | f(*args) | 312 | f(*args, **kwargs) |
1770 | 313 | restarts = [] | 313 | restarts = [] |
1771 | 314 | for path in restart_map: | 314 | for path in restart_map: |
1772 | 315 | if checksums[path] != file_hash(path): | 315 | if checksums[path] != file_hash(path): |
1773 | @@ -339,12 +339,16 @@ | |||
1774 | 339 | def pwgen(length=None): | 339 | def pwgen(length=None): |
1775 | 340 | """Generate a random pasword.""" | 340 | """Generate a random pasword.""" |
1776 | 341 | if length is None: | 341 | if length is None: |
1777 | 342 | # A random length is ok to use a weak PRNG | ||
1778 | 342 | length = random.choice(range(35, 45)) | 343 | length = random.choice(range(35, 45)) |
1779 | 343 | alphanumeric_chars = [ | 344 | alphanumeric_chars = [ |
1780 | 344 | l for l in (string.ascii_letters + string.digits) | 345 | l for l in (string.ascii_letters + string.digits) |
1781 | 345 | if l not in 'l0QD1vAEIOUaeiou'] | 346 | if l not in 'l0QD1vAEIOUaeiou'] |
1782 | 347 | # Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the | ||
1783 | 348 | # actual password | ||
1784 | 349 | random_generator = random.SystemRandom() | ||
1785 | 346 | random_chars = [ | 350 | random_chars = [ |
1787 | 347 | random.choice(alphanumeric_chars) for _ in range(length)] | 351 | random_generator.choice(alphanumeric_chars) for _ in range(length)] |
1788 | 348 | return(''.join(random_chars)) | 352 | return(''.join(random_chars)) |
1789 | 349 | 353 | ||
1790 | 350 | 354 | ||
1791 | @@ -361,7 +365,7 @@ | |||
1792 | 361 | ip_output = (line for line in ip_output if line) | 365 | ip_output = (line for line in ip_output if line) |
1793 | 362 | for line in ip_output: | 366 | for line in ip_output: |
1794 | 363 | if line.split()[1].startswith(int_type): | 367 | if line.split()[1].startswith(int_type): |
1796 | 364 | matched = re.search('.*: (bond[0-9]+\.[0-9]+)@.*', line) | 368 | matched = re.search('.*: (' + int_type + r'[0-9]+\.[0-9]+)@.*', line) |
1797 | 365 | if matched: | 369 | if matched: |
1798 | 366 | interface = matched.groups()[0] | 370 | interface = matched.groups()[0] |
1799 | 367 | else: | 371 | else: |
1800 | 368 | 372 | ||
1801 | === modified file 'hooks/charmhelpers/core/services/helpers.py' | |||
1802 | --- hooks/charmhelpers/core/services/helpers.py 2015-01-26 09:45:23 +0000 | |||
1803 | +++ hooks/charmhelpers/core/services/helpers.py 2015-04-16 21:50:07 +0000 | |||
1804 | @@ -45,12 +45,14 @@ | |||
1805 | 45 | """ | 45 | """ |
1806 | 46 | name = None | 46 | name = None |
1807 | 47 | interface = None | 47 | interface = None |
1808 | 48 | required_keys = [] | ||
1809 | 49 | 48 | ||
1810 | 50 | def __init__(self, name=None, additional_required_keys=None): | 49 | def __init__(self, name=None, additional_required_keys=None): |
1811 | 50 | if not hasattr(self, 'required_keys'): | ||
1812 | 51 | self.required_keys = [] | ||
1813 | 52 | |||
1814 | 51 | if name is not None: | 53 | if name is not None: |
1815 | 52 | self.name = name | 54 | self.name = name |
1817 | 53 | if additional_required_keys is not None: | 55 | if additional_required_keys: |
1818 | 54 | self.required_keys.extend(additional_required_keys) | 56 | self.required_keys.extend(additional_required_keys) |
1819 | 55 | self.get_data() | 57 | self.get_data() |
1820 | 56 | 58 | ||
1821 | @@ -134,7 +136,10 @@ | |||
1822 | 134 | """ | 136 | """ |
1823 | 135 | name = 'db' | 137 | name = 'db' |
1824 | 136 | interface = 'mysql' | 138 | interface = 'mysql' |
1826 | 137 | required_keys = ['host', 'user', 'password', 'database'] | 139 | |
1827 | 140 | def __init__(self, *args, **kwargs): | ||
1828 | 141 | self.required_keys = ['host', 'user', 'password', 'database'] | ||
1829 | 142 | RelationContext.__init__(self, *args, **kwargs) | ||
1830 | 138 | 143 | ||
1831 | 139 | 144 | ||
1832 | 140 | class HttpRelation(RelationContext): | 145 | class HttpRelation(RelationContext): |
1833 | @@ -146,7 +151,10 @@ | |||
1834 | 146 | """ | 151 | """ |
1835 | 147 | name = 'website' | 152 | name = 'website' |
1836 | 148 | interface = 'http' | 153 | interface = 'http' |
1838 | 149 | required_keys = ['host', 'port'] | 154 | |
1839 | 155 | def __init__(self, *args, **kwargs): | ||
1840 | 156 | self.required_keys = ['host', 'port'] | ||
1841 | 157 | RelationContext.__init__(self, *args, **kwargs) | ||
1842 | 150 | 158 | ||
1843 | 151 | def provide_data(self): | 159 | def provide_data(self): |
1844 | 152 | return { | 160 | return { |
1845 | 153 | 161 | ||
1846 | === added file 'hooks/charmhelpers/core/strutils.py' | |||
1847 | --- hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000 | |||
1848 | +++ hooks/charmhelpers/core/strutils.py 2015-04-16 21:50:07 +0000 | |||
1849 | @@ -0,0 +1,42 @@ | |||
1850 | 1 | #!/usr/bin/env python | ||
1851 | 2 | # -*- coding: utf-8 -*- | ||
1852 | 3 | |||
1853 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
1854 | 5 | # | ||
1855 | 6 | # This file is part of charm-helpers. | ||
1856 | 7 | # | ||
1857 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
1858 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1859 | 10 | # published by the Free Software Foundation. | ||
1860 | 11 | # | ||
1861 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
1862 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1863 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1864 | 15 | # GNU Lesser General Public License for more details. | ||
1865 | 16 | # | ||
1866 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
1867 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1868 | 19 | |||
1869 | 20 | import six | ||
1870 | 21 | |||
1871 | 22 | |||
1872 | 23 | def bool_from_string(value): | ||
1873 | 24 | """Interpret string value as boolean. | ||
1874 | 25 | |||
1875 | 26 | Returns True if value translates to True otherwise False. | ||
1876 | 27 | """ | ||
1877 | 28 | if isinstance(value, six.string_types): | ||
1878 | 29 | value = six.text_type(value) | ||
1879 | 30 | else: | ||
1880 | 31 | msg = "Unable to interpret non-string value '%s' as boolean" % (value) | ||
1881 | 32 | raise ValueError(msg) | ||
1882 | 33 | |||
1883 | 34 | value = value.strip().lower() | ||
1884 | 35 | |||
1885 | 36 | if value in ['y', 'yes', 'true', 't', 'on']: | ||
1886 | 37 | return True | ||
1887 | 38 | elif value in ['n', 'no', 'false', 'f', 'off']: | ||
1888 | 39 | return False | ||
1889 | 40 | |||
1890 | 41 | msg = "Unable to interpret string value '%s' as boolean" % (value) | ||
1891 | 42 | raise ValueError(msg) | ||
1892 | 0 | 43 | ||
1893 | === modified file 'hooks/charmhelpers/core/sysctl.py' | |||
1894 | --- hooks/charmhelpers/core/sysctl.py 2015-03-05 10:50:47 +0000 | |||
1895 | +++ hooks/charmhelpers/core/sysctl.py 2015-04-16 21:50:07 +0000 | |||
1896 | @@ -17,8 +17,6 @@ | |||
1897 | 17 | # You should have received a copy of the GNU Lesser General Public License | 17 | # You should have received a copy of the GNU Lesser General Public License |
1898 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
1899 | 19 | 19 | ||
1900 | 20 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
1901 | 21 | |||
1902 | 22 | import yaml | 20 | import yaml |
1903 | 23 | 21 | ||
1904 | 24 | from subprocess import check_call | 22 | from subprocess import check_call |
1905 | @@ -29,6 +27,8 @@ | |||
1906 | 29 | ERROR, | 27 | ERROR, |
1907 | 30 | ) | 28 | ) |
1908 | 31 | 29 | ||
1909 | 30 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
1910 | 31 | |||
1911 | 32 | 32 | ||
1912 | 33 | def create(sysctl_dict, sysctl_file): | 33 | def create(sysctl_dict, sysctl_file): |
1913 | 34 | """Creates a sysctl.conf file from a YAML associative array | 34 | """Creates a sysctl.conf file from a YAML associative array |
1914 | 35 | 35 | ||
1915 | === modified file 'hooks/charmhelpers/core/templating.py' | |||
1916 | --- hooks/charmhelpers/core/templating.py 2015-01-26 09:45:23 +0000 | |||
1917 | +++ hooks/charmhelpers/core/templating.py 2015-04-16 21:50:07 +0000 | |||
1918 | @@ -21,7 +21,7 @@ | |||
1919 | 21 | 21 | ||
1920 | 22 | 22 | ||
1921 | 23 | def render(source, target, context, owner='root', group='root', | 23 | def render(source, target, context, owner='root', group='root', |
1923 | 24 | perms=0o444, templates_dir=None): | 24 | perms=0o444, templates_dir=None, encoding='UTF-8'): |
1924 | 25 | """ | 25 | """ |
1925 | 26 | Render a template. | 26 | Render a template. |
1926 | 27 | 27 | ||
1927 | @@ -64,5 +64,5 @@ | |||
1928 | 64 | level=hookenv.ERROR) | 64 | level=hookenv.ERROR) |
1929 | 65 | raise e | 65 | raise e |
1930 | 66 | content = template.render(context) | 66 | content = template.render(context) |
1933 | 67 | host.mkdir(os.path.dirname(target), owner, group) | 67 | host.mkdir(os.path.dirname(target), owner, group, perms=0o755) |
1934 | 68 | host.write_file(target, content, owner, group, perms) | 68 | host.write_file(target, content.encode(encoding), owner, group, perms) |
1935 | 69 | 69 | ||
1936 | === added file 'hooks/charmhelpers/core/unitdata.py' | |||
1937 | --- hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000 | |||
1938 | +++ hooks/charmhelpers/core/unitdata.py 2015-04-16 21:50:07 +0000 | |||
1939 | @@ -0,0 +1,477 @@ | |||
1940 | 1 | #!/usr/bin/env python | ||
1941 | 2 | # -*- coding: utf-8 -*- | ||
1942 | 3 | # | ||
1943 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
1944 | 5 | # | ||
1945 | 6 | # This file is part of charm-helpers. | ||
1946 | 7 | # | ||
1947 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
1948 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
1949 | 10 | # published by the Free Software Foundation. | ||
1950 | 11 | # | ||
1951 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
1952 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1953 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1954 | 15 | # GNU Lesser General Public License for more details. | ||
1955 | 16 | # | ||
1956 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
1957 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
1958 | 19 | # | ||
1959 | 20 | # | ||
1960 | 21 | # Authors: | ||
1961 | 22 | # Kapil Thangavelu <kapil.foss@gmail.com> | ||
1962 | 23 | # | ||
1963 | 24 | """ | ||
1964 | 25 | Intro | ||
1965 | 26 | ----- | ||
1966 | 27 | |||
1967 | 28 | A simple way to store state in units. This provides a key value | ||
1968 | 29 | storage with support for versioned, transactional operation, | ||
1969 | 30 | and can calculate deltas from previous values to simplify unit logic | ||
1970 | 31 | when processing changes. | ||
1971 | 32 | |||
1972 | 33 | |||
1973 | 34 | Hook Integration | ||
1974 | 35 | ---------------- | ||
1975 | 36 | |||
1976 | 37 | There are several extant frameworks for hook execution, including | ||
1977 | 38 | |||
1978 | 39 | - charmhelpers.core.hookenv.Hooks | ||
1979 | 40 | - charmhelpers.core.services.ServiceManager | ||
1980 | 41 | |||
1981 | 42 | The storage classes are framework agnostic, one simple integration is | ||
1982 | 43 | via the HookData contextmanager. It will record the current hook | ||
1983 | 44 | execution environment (including relation data, config data, etc.), | ||
1984 | 45 | setup a transaction and allow easy access to the changes from | ||
1985 | 46 | previously seen values. One consequence of the integration is the | ||
1986 | 47 | reservation of particular keys ('rels', 'unit', 'env', 'config', | ||
1987 | 48 | 'charm_revisions') for their respective values. | ||
1988 | 49 | |||
1989 | 50 | Here's a fully worked integration example using hookenv.Hooks:: | ||
1990 | 51 | |||
1991 | 52 | from charmhelper.core import hookenv, unitdata | ||
1992 | 53 | |||
1993 | 54 | hook_data = unitdata.HookData() | ||
1994 | 55 | db = unitdata.kv() | ||
1995 | 56 | hooks = hookenv.Hooks() | ||
1996 | 57 | |||
1997 | 58 | @hooks.hook | ||
1998 | 59 | def config_changed(): | ||
1999 | 60 | # Print all changes to configuration from previously seen | ||
2000 | 61 | # values. | ||
2001 | 62 | for changed, (prev, cur) in hook_data.conf.items(): | ||
2002 | 63 | print('config changed', changed, | ||
2003 | 64 | 'previous value', prev, | ||
2004 | 65 | 'current value', cur) | ||
2005 | 66 | |||
2006 | 67 | # Get some unit specific bookeeping | ||
2007 | 68 | if not db.get('pkg_key'): | ||
2008 | 69 | key = urllib.urlopen('https://example.com/pkg_key').read() | ||
2009 | 70 | db.set('pkg_key', key) | ||
2010 | 71 | |||
2011 | 72 | # Directly access all charm config as a mapping. | ||
2012 | 73 | conf = db.getrange('config', True) | ||
2013 | 74 | |||
2014 | 75 | # Directly access all relation data as a mapping | ||
2015 | 76 | rels = db.getrange('rels', True) | ||
2016 | 77 | |||
2017 | 78 | if __name__ == '__main__': | ||
2018 | 79 | with hook_data(): | ||
2019 | 80 | hook.execute() | ||
2020 | 81 | |||
2021 | 82 | |||
2022 | 83 | A more basic integration is via the hook_scope context manager which simply | ||
2023 | 84 | manages transaction scope (and records hook name, and timestamp):: | ||
2024 | 85 | |||
2025 | 86 | >>> from unitdata import kv | ||
2026 | 87 | >>> db = kv() | ||
2027 | 88 | >>> with db.hook_scope('install'): | ||
2028 | 89 | ... # do work, in transactional scope. | ||
2029 | 90 | ... db.set('x', 1) | ||
2030 | 91 | >>> db.get('x') | ||
2031 | 92 | 1 | ||
2032 | 93 | |||
2033 | 94 | |||
2034 | 95 | Usage | ||
2035 | 96 | ----- | ||
2036 | 97 | |||
2037 | 98 | Values are automatically json de/serialized to preserve basic typing | ||
2038 | 99 | and complex data struct capabilities (dicts, lists, ints, booleans, etc). | ||
2039 | 100 | |||
2040 | 101 | Individual values can be manipulated via get/set:: | ||
2041 | 102 | |||
2042 | 103 | >>> kv.set('y', True) | ||
2043 | 104 | >>> kv.get('y') | ||
2044 | 105 | True | ||
2045 | 106 | |||
2046 | 107 | # We can set complex values (dicts, lists) as a single key. | ||
2047 | 108 | >>> kv.set('config', {'a': 1, 'b': True'}) | ||
2048 | 109 | |||
2049 | 110 | # Also supports returning dictionaries as a record which | ||
2050 | 111 | # provides attribute access. | ||
2051 | 112 | >>> config = kv.get('config', record=True) | ||
2052 | 113 | >>> config.b | ||
2053 | 114 | True | ||
2054 | 115 | |||
2055 | 116 | |||
2056 | 117 | Groups of keys can be manipulated with update/getrange:: | ||
2057 | 118 | |||
2058 | 119 | >>> kv.update({'z': 1, 'y': 2}, prefix="gui.") | ||
2059 | 120 | >>> kv.getrange('gui.', strip=True) | ||
2060 | 121 | {'z': 1, 'y': 2} | ||
2061 | 122 | |||
2062 | 123 | When updating values, its very helpful to understand which values | ||
2063 | 124 | have actually changed and how have they changed. The storage | ||
2064 | 125 | provides a delta method to provide for this:: | ||
2065 | 126 | |||
2066 | 127 | >>> data = {'debug': True, 'option': 2} | ||
2067 | 128 | >>> delta = kv.delta(data, 'config.') | ||
2068 | 129 | >>> delta.debug.previous | ||
2069 | 130 | None | ||
2070 | 131 | >>> delta.debug.current | ||
2071 | 132 | True | ||
2072 | 133 | >>> delta | ||
2073 | 134 | {'debug': (None, True), 'option': (None, 2)} | ||
2074 | 135 | |||
2075 | 136 | Note the delta method does not persist the actual change, it needs to | ||
2076 | 137 | be explicitly saved via 'update' method:: | ||
2077 | 138 | |||
2078 | 139 | >>> kv.update(data, 'config.') | ||
2079 | 140 | |||
2080 | 141 | Values modified in the context of a hook scope retain historical values | ||
2081 | 142 | associated to the hookname. | ||
2082 | 143 | |||
2083 | 144 | >>> with db.hook_scope('config-changed'): | ||
2084 | 145 | ... db.set('x', 42) | ||
2085 | 146 | >>> db.gethistory('x') | ||
2086 | 147 | [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'), | ||
2087 | 148 | (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')] | ||
2088 | 149 | |||
2089 | 150 | """ | ||
2090 | 151 | |||
2091 | 152 | import collections | ||
2092 | 153 | import contextlib | ||
2093 | 154 | import datetime | ||
2094 | 155 | import json | ||
2095 | 156 | import os | ||
2096 | 157 | import pprint | ||
2097 | 158 | import sqlite3 | ||
2098 | 159 | import sys | ||
2099 | 160 | |||
2100 | 161 | __author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>' | ||
2101 | 162 | |||
2102 | 163 | |||
2103 | 164 | class Storage(object): | ||
2104 | 165 | """Simple key value database for local unit state within charms. | ||
2105 | 166 | |||
2106 | 167 | Modifications are automatically committed at hook exit. That's | ||
2107 | 168 | currently regardless of exit code. | ||
2108 | 169 | |||
2109 | 170 | To support dicts, lists, integer, floats, and booleans values | ||
2110 | 171 | are automatically json encoded/decoded. | ||
2111 | 172 | """ | ||
2112 | 173 | def __init__(self, path=None): | ||
2113 | 174 | self.db_path = path | ||
2114 | 175 | if path is None: | ||
2115 | 176 | self.db_path = os.path.join( | ||
2116 | 177 | os.environ.get('CHARM_DIR', ''), '.unit-state.db') | ||
2117 | 178 | self.conn = sqlite3.connect('%s' % self.db_path) | ||
2118 | 179 | self.cursor = self.conn.cursor() | ||
2119 | 180 | self.revision = None | ||
2120 | 181 | self._closed = False | ||
2121 | 182 | self._init() | ||
2122 | 183 | |||
2123 | 184 | def close(self): | ||
2124 | 185 | if self._closed: | ||
2125 | 186 | return | ||
2126 | 187 | self.flush(False) | ||
2127 | 188 | self.cursor.close() | ||
2128 | 189 | self.conn.close() | ||
2129 | 190 | self._closed = True | ||
2130 | 191 | |||
2131 | 192 | def _scoped_query(self, stmt, params=None): | ||
2132 | 193 | if params is None: | ||
2133 | 194 | params = [] | ||
2134 | 195 | return stmt, params | ||
2135 | 196 | |||
2136 | 197 | def get(self, key, default=None, record=False): | ||
2137 | 198 | self.cursor.execute( | ||
2138 | 199 | *self._scoped_query( | ||
2139 | 200 | 'select data from kv where key=?', [key])) | ||
2140 | 201 | result = self.cursor.fetchone() | ||
2141 | 202 | if not result: | ||
2142 | 203 | return default | ||
2143 | 204 | if record: | ||
2144 | 205 | return Record(json.loads(result[0])) | ||
2145 | 206 | return json.loads(result[0]) | ||
2146 | 207 | |||
2147 | 208 | def getrange(self, key_prefix, strip=False): | ||
2148 | 209 | stmt = "select key, data from kv where key like '%s%%'" % key_prefix | ||
2149 | 210 | self.cursor.execute(*self._scoped_query(stmt)) | ||
2150 | 211 | result = self.cursor.fetchall() | ||
2151 | 212 | |||
2152 | 213 | if not result: | ||
2153 | 214 | return None | ||
2154 | 215 | if not strip: | ||
2155 | 216 | key_prefix = '' | ||
2156 | 217 | return dict([ | ||
2157 | 218 | (k[len(key_prefix):], json.loads(v)) for k, v in result]) | ||
2158 | 219 | |||
2159 | 220 | def update(self, mapping, prefix=""): | ||
2160 | 221 | for k, v in mapping.items(): | ||
2161 | 222 | self.set("%s%s" % (prefix, k), v) | ||
2162 | 223 | |||
2163 | 224 | def unset(self, key): | ||
2164 | 225 | self.cursor.execute('delete from kv where key=?', [key]) | ||
2165 | 226 | if self.revision and self.cursor.rowcount: | ||
2166 | 227 | self.cursor.execute( | ||
2167 | 228 | 'insert into kv_revisions values (?, ?, ?)', | ||
2168 | 229 | [key, self.revision, json.dumps('DELETED')]) | ||
2169 | 230 | |||
2170 | 231 | def set(self, key, value): | ||
2171 | 232 | serialized = json.dumps(value) | ||
2172 | 233 | |||
2173 | 234 | self.cursor.execute( | ||
2174 | 235 | 'select data from kv where key=?', [key]) | ||
2175 | 236 | exists = self.cursor.fetchone() | ||
2176 | 237 | |||
2177 | 238 | # Skip mutations to the same value | ||
2178 | 239 | if exists: | ||
2179 | 240 | if exists[0] == serialized: | ||
2180 | 241 | return value | ||
2181 | 242 | |||
2182 | 243 | if not exists: | ||
2183 | 244 | self.cursor.execute( | ||
2184 | 245 | 'insert into kv (key, data) values (?, ?)', | ||
2185 | 246 | (key, serialized)) | ||
2186 | 247 | else: | ||
2187 | 248 | self.cursor.execute(''' | ||
2188 | 249 | update kv | ||
2189 | 250 | set data = ? | ||
2190 | 251 | where key = ?''', [serialized, key]) | ||
2191 | 252 | |||
2192 | 253 | # Save | ||
2193 | 254 | if not self.revision: | ||
2194 | 255 | return value | ||
2195 | 256 | |||
2196 | 257 | self.cursor.execute( | ||
2197 | 258 | 'select 1 from kv_revisions where key=? and revision=?', | ||
2198 | 259 | [key, self.revision]) | ||
2199 | 260 | exists = self.cursor.fetchone() | ||
2200 | 261 | |||
2201 | 262 | if not exists: | ||
2202 | 263 | self.cursor.execute( | ||
2203 | 264 | '''insert into kv_revisions ( | ||
2204 | 265 | revision, key, data) values (?, ?, ?)''', | ||
2205 | 266 | (self.revision, key, serialized)) | ||
2206 | 267 | else: | ||
2207 | 268 | self.cursor.execute( | ||
2208 | 269 | ''' | ||
2209 | 270 | update kv_revisions | ||
2210 | 271 | set data = ? | ||
2211 | 272 | where key = ? | ||
2212 | 273 | and revision = ?''', | ||
2213 | 274 | [serialized, key, self.revision]) | ||
2214 | 275 | |||
2215 | 276 | return value | ||
2216 | 277 | |||
2217 | 278 | def delta(self, mapping, prefix): | ||
2218 | 279 | """ | ||
2219 | 280 | return a delta containing values that have changed. | ||
2220 | 281 | """ | ||
2221 | 282 | previous = self.getrange(prefix, strip=True) | ||
2222 | 283 | if not previous: | ||
2223 | 284 | pk = set() | ||
2224 | 285 | else: | ||
2225 | 286 | pk = set(previous.keys()) | ||
2226 | 287 | ck = set(mapping.keys()) | ||
2227 | 288 | delta = DeltaSet() | ||
2228 | 289 | |||
2229 | 290 | # added | ||
2230 | 291 | for k in ck.difference(pk): | ||
2231 | 292 | delta[k] = Delta(None, mapping[k]) | ||
2232 | 293 | |||
2233 | 294 | # removed | ||
2234 | 295 | for k in pk.difference(ck): | ||
2235 | 296 | delta[k] = Delta(previous[k], None) | ||
2236 | 297 | |||
2237 | 298 | # changed | ||
2238 | 299 | for k in pk.intersection(ck): | ||
2239 | 300 | c = mapping[k] | ||
2240 | 301 | p = previous[k] | ||
2241 | 302 | if c != p: | ||
2242 | 303 | delta[k] = Delta(p, c) | ||
2243 | 304 | |||
2244 | 305 | return delta | ||
2245 | 306 | |||
2246 | 307 | @contextlib.contextmanager | ||
2247 | 308 | def hook_scope(self, name=""): | ||
2248 | 309 | """Scope all future interactions to the current hook execution | ||
2249 | 310 | revision.""" | ||
2250 | 311 | assert not self.revision | ||
2251 | 312 | self.cursor.execute( | ||
2252 | 313 | 'insert into hooks (hook, date) values (?, ?)', | ||
2253 | 314 | (name or sys.argv[0], | ||
2254 | 315 | datetime.datetime.utcnow().isoformat())) | ||
2255 | 316 | self.revision = self.cursor.lastrowid | ||
2256 | 317 | try: | ||
2257 | 318 | yield self.revision | ||
2258 | 319 | self.revision = None | ||
2259 | 320 | except: | ||
2260 | 321 | self.flush(False) | ||
2261 | 322 | self.revision = None | ||
2262 | 323 | raise | ||
2263 | 324 | else: | ||
2264 | 325 | self.flush() | ||
2265 | 326 | |||
2266 | 327 | def flush(self, save=True): | ||
2267 | 328 | if save: | ||
2268 | 329 | self.conn.commit() | ||
2269 | 330 | elif self._closed: | ||
2270 | 331 | return | ||
2271 | 332 | else: | ||
2272 | 333 | self.conn.rollback() | ||
2273 | 334 | |||
2274 | 335 | def _init(self): | ||
2275 | 336 | self.cursor.execute(''' | ||
2276 | 337 | create table if not exists kv ( | ||
2277 | 338 | key text, | ||
2278 | 339 | data text, | ||
2279 | 340 | primary key (key) | ||
2280 | 341 | )''') | ||
2281 | 342 | self.cursor.execute(''' | ||
2282 | 343 | create table if not exists kv_revisions ( | ||
2283 | 344 | key text, | ||
2284 | 345 | revision integer, | ||
2285 | 346 | data text, | ||
2286 | 347 | primary key (key, revision) | ||
2287 | 348 | )''') | ||
2288 | 349 | self.cursor.execute(''' | ||
2289 | 350 | create table if not exists hooks ( | ||
2290 | 351 | version integer primary key autoincrement, | ||
2291 | 352 | hook text, | ||
2292 | 353 | date text | ||
2293 | 354 | )''') | ||
2294 | 355 | self.conn.commit() | ||
2295 | 356 | |||
2296 | 357 | def gethistory(self, key, deserialize=False): | ||
2297 | 358 | self.cursor.execute( | ||
2298 | 359 | ''' | ||
2299 | 360 | select kv.revision, kv.key, kv.data, h.hook, h.date | ||
2300 | 361 | from kv_revisions kv, | ||
2301 | 362 | hooks h | ||
2302 | 363 | where kv.key=? | ||
2303 | 364 | and kv.revision = h.version | ||
2304 | 365 | ''', [key]) | ||
2305 | 366 | if deserialize is False: | ||
2306 | 367 | return self.cursor.fetchall() | ||
2307 | 368 | return map(_parse_history, self.cursor.fetchall()) | ||
2308 | 369 | |||
2309 | 370 | def debug(self, fh=sys.stderr): | ||
2310 | 371 | self.cursor.execute('select * from kv') | ||
2311 | 372 | pprint.pprint(self.cursor.fetchall(), stream=fh) | ||
2312 | 373 | self.cursor.execute('select * from kv_revisions') | ||
2313 | 374 | pprint.pprint(self.cursor.fetchall(), stream=fh) | ||
2314 | 375 | |||
2315 | 376 | |||
2316 | 377 | def _parse_history(d): | ||
2317 | 378 | return (d[0], d[1], json.loads(d[2]), d[3], | ||
2318 | 379 | datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f")) | ||
2319 | 380 | |||
2320 | 381 | |||
2321 | 382 | class HookData(object): | ||
2322 | 383 | """Simple integration for existing hook exec frameworks. | ||
2323 | 384 | |||
2324 | 385 | Records all unit information, and stores deltas for processing | ||
2325 | 386 | by the hook. | ||
2326 | 387 | |||
2327 | 388 | Sample:: | ||
2328 | 389 | |||
2329 | 390 | from charmhelper.core import hookenv, unitdata | ||
2330 | 391 | |||
2331 | 392 | changes = unitdata.HookData() | ||
2332 | 393 | db = unitdata.kv() | ||
2333 | 394 | hooks = hookenv.Hooks() | ||
2334 | 395 | |||
2335 | 396 | @hooks.hook | ||
2336 | 397 | def config_changed(): | ||
2337 | 398 | # View all changes to configuration | ||
2338 | 399 | for changed, (prev, cur) in changes.conf.items(): | ||
2339 | 400 | print('config changed', changed, | ||
2340 | 401 | 'previous value', prev, | ||
2341 | 402 | 'current value', cur) | ||
2342 | 403 | |||
2343 | 404 | # Get some unit specific bookeeping | ||
2344 | 405 | if not db.get('pkg_key'): | ||
2345 | 406 | key = urllib.urlopen('https://example.com/pkg_key').read() | ||
2346 | 407 | db.set('pkg_key', key) | ||
2347 | 408 | |||
2348 | 409 | if __name__ == '__main__': | ||
2349 | 410 | with changes(): | ||
2350 | 411 | hook.execute() | ||
2351 | 412 | |||
2352 | 413 | """ | ||
2353 | 414 | def __init__(self): | ||
2354 | 415 | self.kv = kv() | ||
2355 | 416 | self.conf = None | ||
2356 | 417 | self.rels = None | ||
2357 | 418 | |||
2358 | 419 | @contextlib.contextmanager | ||
2359 | 420 | def __call__(self): | ||
2360 | 421 | from charmhelpers.core import hookenv | ||
2361 | 422 | hook_name = hookenv.hook_name() | ||
2362 | 423 | |||
2363 | 424 | with self.kv.hook_scope(hook_name): | ||
2364 | 425 | self._record_charm_version(hookenv.charm_dir()) | ||
2365 | 426 | delta_config, delta_relation = self._record_hook(hookenv) | ||
2366 | 427 | yield self.kv, delta_config, delta_relation | ||
2367 | 428 | |||
2368 | 429 | def _record_charm_version(self, charm_dir): | ||
2369 | 430 | # Record revisions.. charm revisions are meaningless | ||
2370 | 431 | # to charm authors as they don't control the revision. | ||
2371 | 432 | # so logic dependnent on revision is not particularly | ||
2372 | 433 | # useful, however it is useful for debugging analysis. | ||
2373 | 434 | charm_rev = open( | ||
2374 | 435 | os.path.join(charm_dir, 'revision')).read().strip() | ||
2375 | 436 | charm_rev = charm_rev or '0' | ||
2376 | 437 | revs = self.kv.get('charm_revisions', []) | ||
2377 | 438 | if charm_rev not in revs: | ||
2378 | 439 | revs.append(charm_rev.strip() or '0') | ||
2379 | 440 | self.kv.set('charm_revisions', revs) | ||
2380 | 441 | |||
2381 | 442 | def _record_hook(self, hookenv): | ||
2382 | 443 | data = hookenv.execution_environment() | ||
2383 | 444 | self.conf = conf_delta = self.kv.delta(data['conf'], 'config') | ||
2384 | 445 | self.rels = rels_delta = self.kv.delta(data['rels'], 'rels') | ||
2385 | 446 | self.kv.set('env', dict(data['env'])) | ||
2386 | 447 | self.kv.set('unit', data['unit']) | ||
2387 | 448 | self.kv.set('relid', data.get('relid')) | ||
2388 | 449 | return conf_delta, rels_delta | ||
2389 | 450 | |||
2390 | 451 | |||
2391 | 452 | class Record(dict): | ||
2392 | 453 | |||
2393 | 454 | __slots__ = () | ||
2394 | 455 | |||
2395 | 456 | def __getattr__(self, k): | ||
2396 | 457 | if k in self: | ||
2397 | 458 | return self[k] | ||
2398 | 459 | raise AttributeError(k) | ||
2399 | 460 | |||
2400 | 461 | |||
2401 | 462 | class DeltaSet(Record): | ||
2402 | 463 | |||
2403 | 464 | __slots__ = () | ||
2404 | 465 | |||
2405 | 466 | |||
2406 | 467 | Delta = collections.namedtuple('Delta', ['previous', 'current']) | ||
2407 | 468 | |||
2408 | 469 | |||
2409 | 470 | _KV = None | ||
2410 | 471 | |||
2411 | 472 | |||
2412 | 473 | def kv(): | ||
2413 | 474 | global _KV | ||
2414 | 475 | if _KV is None: | ||
2415 | 476 | _KV = Storage() | ||
2416 | 477 | return _KV | ||
2417 | 0 | 478 | ||
2418 | === modified file 'hooks/charmhelpers/fetch/archiveurl.py' | |||
2419 | --- hooks/charmhelpers/fetch/archiveurl.py 2015-01-26 09:45:23 +0000 | |||
2420 | +++ hooks/charmhelpers/fetch/archiveurl.py 2015-04-16 21:50:07 +0000 | |||
2421 | @@ -18,6 +18,16 @@ | |||
2422 | 18 | import hashlib | 18 | import hashlib |
2423 | 19 | import re | 19 | import re |
2424 | 20 | 20 | ||
2425 | 21 | from charmhelpers.fetch import ( | ||
2426 | 22 | BaseFetchHandler, | ||
2427 | 23 | UnhandledSource | ||
2428 | 24 | ) | ||
2429 | 25 | from charmhelpers.payload.archive import ( | ||
2430 | 26 | get_archive_handler, | ||
2431 | 27 | extract, | ||
2432 | 28 | ) | ||
2433 | 29 | from charmhelpers.core.host import mkdir, check_hash | ||
2434 | 30 | |||
2435 | 21 | import six | 31 | import six |
2436 | 22 | if six.PY3: | 32 | if six.PY3: |
2437 | 23 | from urllib.request import ( | 33 | from urllib.request import ( |
2438 | @@ -35,16 +45,6 @@ | |||
2439 | 35 | ) | 45 | ) |
2440 | 36 | from urlparse import urlparse, urlunparse, parse_qs | 46 | from urlparse import urlparse, urlunparse, parse_qs |
2441 | 37 | 47 | ||
2442 | 38 | from charmhelpers.fetch import ( | ||
2443 | 39 | BaseFetchHandler, | ||
2444 | 40 | UnhandledSource | ||
2445 | 41 | ) | ||
2446 | 42 | from charmhelpers.payload.archive import ( | ||
2447 | 43 | get_archive_handler, | ||
2448 | 44 | extract, | ||
2449 | 45 | ) | ||
2450 | 46 | from charmhelpers.core.host import mkdir, check_hash | ||
2451 | 47 | |||
2452 | 48 | 48 | ||
2453 | 49 | def splituser(host): | 49 | def splituser(host): |
2454 | 50 | '''urllib.splituser(), but six's support of this seems broken''' | 50 | '''urllib.splituser(), but six's support of this seems broken''' |
2455 | 51 | 51 | ||
2456 | === modified file 'hooks/charmhelpers/fetch/giturl.py' | |||
2457 | --- hooks/charmhelpers/fetch/giturl.py 2015-01-26 09:45:23 +0000 | |||
2458 | +++ hooks/charmhelpers/fetch/giturl.py 2015-04-16 21:50:07 +0000 | |||
2459 | @@ -32,7 +32,7 @@ | |||
2460 | 32 | apt_install("python-git") | 32 | apt_install("python-git") |
2461 | 33 | from git import Repo | 33 | from git import Repo |
2462 | 34 | 34 | ||
2464 | 35 | from git.exc import GitCommandError | 35 | from git.exc import GitCommandError # noqa E402 |
2465 | 36 | 36 | ||
2466 | 37 | 37 | ||
2467 | 38 | class GitUrlFetchHandler(BaseFetchHandler): | 38 | class GitUrlFetchHandler(BaseFetchHandler): |
2468 | 39 | 39 | ||
2469 | === modified file 'hooks/glance_relations.py' | |||
2470 | --- hooks/glance_relations.py 2015-01-22 16:26:28 +0000 | |||
2471 | +++ hooks/glance_relations.py 2015-04-16 21:50:07 +0000 | |||
2472 | @@ -1,18 +1,20 @@ | |||
2473 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
2474 | 2 | import sys | ||
2475 | 3 | |||
2476 | 2 | from subprocess import ( | 4 | from subprocess import ( |
2477 | 5 | call, | ||
2478 | 3 | check_call, | 6 | check_call, |
2479 | 4 | call | ||
2480 | 5 | ) | 7 | ) |
2481 | 6 | import sys | ||
2482 | 7 | 8 | ||
2483 | 8 | from glance_utils import ( | 9 | from glance_utils import ( |
2484 | 9 | do_openstack_upgrade, | 10 | do_openstack_upgrade, |
2485 | 11 | git_install, | ||
2486 | 10 | migrate_database, | 12 | migrate_database, |
2487 | 11 | register_configs, | 13 | register_configs, |
2488 | 12 | restart_map, | 14 | restart_map, |
2489 | 13 | services, | 15 | services, |
2490 | 14 | CLUSTER_RES, | 16 | CLUSTER_RES, |
2492 | 15 | PACKAGES, | 17 | determine_packages, |
2493 | 16 | SERVICES, | 18 | SERVICES, |
2494 | 17 | CHARM, | 19 | CHARM, |
2495 | 18 | GLANCE_REGISTRY_CONF, | 20 | GLANCE_REGISTRY_CONF, |
2496 | @@ -41,6 +43,7 @@ | |||
2497 | 41 | ) | 43 | ) |
2498 | 42 | from charmhelpers.core.host import ( | 44 | from charmhelpers.core.host import ( |
2499 | 43 | restart_on_change, | 45 | restart_on_change, |
2500 | 46 | service_reload, | ||
2501 | 44 | service_stop, | 47 | service_stop, |
2502 | 45 | ) | 48 | ) |
2503 | 46 | from charmhelpers.fetch import ( | 49 | from charmhelpers.fetch import ( |
2504 | @@ -53,16 +56,19 @@ | |||
2505 | 53 | get_hacluster_config | 56 | get_hacluster_config |
2506 | 54 | ) | 57 | ) |
2507 | 55 | from charmhelpers.contrib.openstack.utils import ( | 58 | from charmhelpers.contrib.openstack.utils import ( |
2508 | 59 | config_value_changed, | ||
2509 | 56 | configure_installation_source, | 60 | configure_installation_source, |
2511 | 57 | get_os_codename_package, | 61 | git_install_requested, |
2512 | 62 | lsb_release, | ||
2513 | 58 | openstack_upgrade_available, | 63 | openstack_upgrade_available, |
2516 | 59 | lsb_release, | 64 | os_release, |
2517 | 60 | sync_db_with_multi_ipv6_addresses | 65 | sync_db_with_multi_ipv6_addresses, |
2518 | 61 | ) | 66 | ) |
2519 | 62 | from charmhelpers.contrib.storage.linux.ceph import ( | 67 | from charmhelpers.contrib.storage.linux.ceph import ( |
2520 | 63 | ensure_ceph_keyring, | 68 | ensure_ceph_keyring, |
2521 | 64 | CephBrokerRq, | 69 | CephBrokerRq, |
2522 | 65 | CephBrokerRsp, | 70 | CephBrokerRsp, |
2523 | 71 | delete_keyring, | ||
2524 | 66 | ) | 72 | ) |
2525 | 67 | from charmhelpers.payload.execd import ( | 73 | from charmhelpers.payload.execd import ( |
2526 | 68 | execd_preinstall | 74 | execd_preinstall |
2527 | @@ -100,7 +106,9 @@ | |||
2528 | 100 | configure_installation_source(src) | 106 | configure_installation_source(src) |
2529 | 101 | 107 | ||
2530 | 102 | apt_update(fatal=True) | 108 | apt_update(fatal=True) |
2532 | 103 | apt_install(PACKAGES, fatal=True) | 109 | apt_install(determine_packages(), fatal=True) |
2533 | 110 | |||
2534 | 111 | git_install(config('openstack-origin-git')) | ||
2535 | 104 | 112 | ||
2536 | 105 | for service in SERVICES: | 113 | for service in SERVICES: |
2537 | 106 | service_stop(service) | 114 | service_stop(service) |
2538 | @@ -140,7 +148,7 @@ | |||
2539 | 140 | @hooks.hook('shared-db-relation-changed') | 148 | @hooks.hook('shared-db-relation-changed') |
2540 | 141 | @restart_on_change(restart_map()) | 149 | @restart_on_change(restart_map()) |
2541 | 142 | def db_changed(): | 150 | def db_changed(): |
2543 | 143 | rel = get_os_codename_package("glance-common") | 151 | rel = os_release('glance-common') |
2544 | 144 | 152 | ||
2545 | 145 | if 'shared-db' not in CONFIGS.complete_contexts(): | 153 | if 'shared-db' not in CONFIGS.complete_contexts(): |
2546 | 146 | juju_log('shared-db relation incomplete. Peer not ready?') | 154 | juju_log('shared-db relation incomplete. Peer not ready?') |
2547 | @@ -163,7 +171,8 @@ | |||
2548 | 163 | status = call(['glance-manage', 'db_version']) | 171 | status = call(['glance-manage', 'db_version']) |
2549 | 164 | if status != 0: | 172 | if status != 0: |
2550 | 165 | juju_log('Setting version_control to 0') | 173 | juju_log('Setting version_control to 0') |
2552 | 166 | check_call(["glance-manage", "version_control", "0"]) | 174 | cmd = ["glance-manage", "version_control", "0"] |
2553 | 175 | check_call(cmd) | ||
2554 | 167 | 176 | ||
2555 | 168 | juju_log('Cluster leader, performing db sync') | 177 | juju_log('Cluster leader, performing db sync') |
2556 | 169 | migrate_database() | 178 | migrate_database() |
2557 | @@ -172,7 +181,7 @@ | |||
2558 | 172 | @hooks.hook('pgsql-db-relation-changed') | 181 | @hooks.hook('pgsql-db-relation-changed') |
2559 | 173 | @restart_on_change(restart_map()) | 182 | @restart_on_change(restart_map()) |
2560 | 174 | def pgsql_db_changed(): | 183 | def pgsql_db_changed(): |
2562 | 175 | rel = get_os_codename_package("glance-common") | 184 | rel = os_release('glance-common') |
2563 | 176 | 185 | ||
2564 | 177 | if 'pgsql-db' not in CONFIGS.complete_contexts(): | 186 | if 'pgsql-db' not in CONFIGS.complete_contexts(): |
2565 | 178 | juju_log('pgsql-db relation incomplete. Peer not ready?') | 187 | juju_log('pgsql-db relation incomplete. Peer not ready?') |
2566 | @@ -188,7 +197,8 @@ | |||
2567 | 188 | status = call(['glance-manage', 'db_version']) | 197 | status = call(['glance-manage', 'db_version']) |
2568 | 189 | if status != 0: | 198 | if status != 0: |
2569 | 190 | juju_log('Setting version_control to 0') | 199 | juju_log('Setting version_control to 0') |
2571 | 191 | check_call(["glance-manage", "version_control", "0"]) | 200 | cmd = ["glance-manage", "version_control", "0"] |
2572 | 201 | check_call(cmd) | ||
2573 | 192 | 202 | ||
2574 | 193 | juju_log('Cluster leader, performing db sync') | 203 | juju_log('Cluster leader, performing db sync') |
2575 | 194 | migrate_database() | 204 | migrate_database() |
2576 | @@ -263,6 +273,13 @@ | |||
2577 | 263 | juju_log("Request(s) sent to Ceph broker (rid=%s)" % (rid)) | 273 | juju_log("Request(s) sent to Ceph broker (rid=%s)" % (rid)) |
2578 | 264 | 274 | ||
2579 | 265 | 275 | ||
2580 | 276 | @hooks.hook('ceph-relation-broken') | ||
2581 | 277 | def ceph_broken(): | ||
2582 | 278 | service = service_name() | ||
2583 | 279 | delete_keyring(service=service) | ||
2584 | 280 | CONFIGS.write_all() | ||
2585 | 281 | |||
2586 | 282 | |||
2587 | 266 | @hooks.hook('identity-service-relation-joined') | 283 | @hooks.hook('identity-service-relation-joined') |
2588 | 267 | def keystone_joined(relation_id=None): | 284 | def keystone_joined(relation_id=None): |
2589 | 268 | public_url = '{}:9292'.format(canonical_url(CONFIGS, PUBLIC)) | 285 | public_url = '{}:9292'.format(canonical_url(CONFIGS, PUBLIC)) |
2590 | @@ -308,9 +325,13 @@ | |||
2591 | 308 | sync_db_with_multi_ipv6_addresses(config('database'), | 325 | sync_db_with_multi_ipv6_addresses(config('database'), |
2592 | 309 | config('database-user')) | 326 | config('database-user')) |
2593 | 310 | 327 | ||
2597 | 311 | if openstack_upgrade_available('glance-common'): | 328 | if git_install_requested(): |
2598 | 312 | juju_log('Upgrading OpenStack release') | 329 | if config_value_changed('openstack-origin-git'): |
2599 | 313 | do_openstack_upgrade(CONFIGS) | 330 | git_install(config('openstack-origin-git')) |
2600 | 331 | else: | ||
2601 | 332 | if openstack_upgrade_available('glance-common'): | ||
2602 | 333 | juju_log('Upgrading OpenStack release') | ||
2603 | 334 | do_openstack_upgrade(CONFIGS) | ||
2604 | 314 | 335 | ||
2605 | 315 | open_port(9292) | 336 | open_port(9292) |
2606 | 316 | configure_https() | 337 | configure_https() |
2607 | @@ -354,7 +375,7 @@ | |||
2608 | 354 | @hooks.hook('upgrade-charm') | 375 | @hooks.hook('upgrade-charm') |
2609 | 355 | @restart_on_change(restart_map(), stopstart=True) | 376 | @restart_on_change(restart_map(), stopstart=True) |
2610 | 356 | def upgrade_charm(): | 377 | def upgrade_charm(): |
2612 | 357 | apt_install(filter_installed_packages(PACKAGES), fatal=True) | 378 | apt_install(filter_installed_packages(determine_packages()), fatal=True) |
2613 | 358 | configure_https() | 379 | configure_https() |
2614 | 359 | update_nrpe_config() | 380 | update_nrpe_config() |
2615 | 360 | CONFIGS.write_all() | 381 | CONFIGS.write_all() |
2616 | @@ -433,8 +454,7 @@ | |||
2617 | 433 | [image_service_joined(rid) for rid in relation_ids('image-service')] | 454 | [image_service_joined(rid) for rid in relation_ids('image-service')] |
2618 | 434 | 455 | ||
2619 | 435 | 456 | ||
2622 | 436 | @hooks.hook('ceph-relation-broken', | 457 | @hooks.hook('identity-service-relation-broken', |
2621 | 437 | 'identity-service-relation-broken', | ||
2623 | 438 | 'object-store-relation-broken', | 458 | 'object-store-relation-broken', |
2624 | 439 | 'shared-db-relation-broken', | 459 | 'shared-db-relation-broken', |
2625 | 440 | 'pgsql-db-relation-broken') | 460 | 'pgsql-db-relation-broken') |
2626 | @@ -455,6 +475,10 @@ | |||
2627 | 455 | cmd = ['a2dissite', 'openstack_https_frontend'] | 475 | cmd = ['a2dissite', 'openstack_https_frontend'] |
2628 | 456 | check_call(cmd) | 476 | check_call(cmd) |
2629 | 457 | 477 | ||
2630 | 478 | # TODO: improve this by checking if local CN certs are available | ||
2631 | 479 | # first then checking reload status (see LP #1433114). | ||
2632 | 480 | service_reload('apache2', restart_on_failure=True) | ||
2633 | 481 | |||
2634 | 458 | for r_id in relation_ids('identity-service'): | 482 | for r_id in relation_ids('identity-service'): |
2635 | 459 | keystone_joined(relation_id=r_id) | 483 | keystone_joined(relation_id=r_id) |
2636 | 460 | for r_id in relation_ids('image-service'): | 484 | for r_id in relation_ids('image-service'): |
2637 | @@ -484,7 +508,9 @@ | |||
2638 | 484 | hostname = nrpe.get_nagios_hostname() | 508 | hostname = nrpe.get_nagios_hostname() |
2639 | 485 | current_unit = nrpe.get_nagios_unit_name() | 509 | current_unit = nrpe.get_nagios_unit_name() |
2640 | 486 | nrpe_setup = nrpe.NRPE(hostname=hostname) | 510 | nrpe_setup = nrpe.NRPE(hostname=hostname) |
2641 | 511 | nrpe.copy_nrpe_checks() | ||
2642 | 487 | nrpe.add_init_service_checks(nrpe_setup, services(), current_unit) | 512 | nrpe.add_init_service_checks(nrpe_setup, services(), current_unit) |
2643 | 513 | nrpe.add_haproxy_checks(nrpe_setup, current_unit) | ||
2644 | 488 | nrpe_setup.write() | 514 | nrpe_setup.write() |
2645 | 489 | 515 | ||
2646 | 490 | 516 | ||
2647 | 491 | 517 | ||
2648 | === modified file 'hooks/glance_utils.py' | |||
2649 | --- hooks/glance_utils.py 2015-01-08 10:02:48 +0000 | |||
2650 | +++ hooks/glance_utils.py 2015-04-16 21:50:07 +0000 | |||
2651 | @@ -1,6 +1,7 @@ | |||
2652 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
2653 | 2 | 2 | ||
2654 | 3 | import os | 3 | import os |
2655 | 4 | import shutil | ||
2656 | 4 | import subprocess | 5 | import subprocess |
2657 | 5 | 6 | ||
2658 | 6 | import glance_contexts | 7 | import glance_contexts |
2659 | @@ -14,21 +15,27 @@ | |||
2660 | 14 | add_source) | 15 | add_source) |
2661 | 15 | 16 | ||
2662 | 16 | from charmhelpers.core.hookenv import ( | 17 | from charmhelpers.core.hookenv import ( |
2663 | 18 | charm_dir, | ||
2664 | 17 | config, | 19 | config, |
2665 | 18 | log, | 20 | log, |
2666 | 19 | relation_ids, | 21 | relation_ids, |
2667 | 20 | service_name) | 22 | service_name) |
2668 | 21 | 23 | ||
2669 | 22 | from charmhelpers.core.host import ( | 24 | from charmhelpers.core.host import ( |
2670 | 25 | adduser, | ||
2671 | 26 | add_group, | ||
2672 | 27 | add_user_to_group, | ||
2673 | 23 | mkdir, | 28 | mkdir, |
2674 | 24 | service_stop, | 29 | service_stop, |
2675 | 25 | service_start, | 30 | service_start, |
2677 | 26 | lsb_release | 31 | service_restart, |
2678 | 32 | lsb_release, | ||
2679 | 33 | write_file, | ||
2680 | 27 | ) | 34 | ) |
2681 | 28 | 35 | ||
2682 | 29 | from charmhelpers.contrib.openstack import ( | 36 | from charmhelpers.contrib.openstack import ( |
2683 | 30 | templating, | 37 | templating, |
2685 | 31 | context, ) | 38 | context,) |
2686 | 32 | 39 | ||
2687 | 33 | from charmhelpers.contrib.hahelpers.cluster import ( | 40 | from charmhelpers.contrib.hahelpers.cluster import ( |
2688 | 34 | eligible_leader, | 41 | eligible_leader, |
2689 | @@ -37,8 +44,14 @@ | |||
2690 | 37 | from charmhelpers.contrib.openstack.alternatives import install_alternative | 44 | from charmhelpers.contrib.openstack.alternatives import install_alternative |
2691 | 38 | from charmhelpers.contrib.openstack.utils import ( | 45 | from charmhelpers.contrib.openstack.utils import ( |
2692 | 39 | get_os_codename_install_source, | 46 | get_os_codename_install_source, |
2695 | 40 | get_os_codename_package, | 47 | git_install_requested, |
2696 | 41 | configure_installation_source) | 48 | git_clone_and_install, |
2697 | 49 | git_src_dir, | ||
2698 | 50 | configure_installation_source, | ||
2699 | 51 | os_release, | ||
2700 | 52 | ) | ||
2701 | 53 | |||
2702 | 54 | from charmhelpers.core.templating import render | ||
2703 | 42 | 55 | ||
2704 | 43 | CLUSTER_RES = "grp_glance_vips" | 56 | CLUSTER_RES = "grp_glance_vips" |
2705 | 44 | 57 | ||
2706 | @@ -46,8 +59,27 @@ | |||
2707 | 46 | "apache2", "glance", "python-mysqldb", "python-swiftclient", | 59 | "apache2", "glance", "python-mysqldb", "python-swiftclient", |
2708 | 47 | "python-psycopg2", "python-keystone", "python-six", "uuid", "haproxy", ] | 60 | "python-psycopg2", "python-keystone", "python-six", "uuid", "haproxy", ] |
2709 | 48 | 61 | ||
2710 | 62 | BASE_GIT_PACKAGES = [ | ||
2711 | 63 | 'libxml2-dev', | ||
2712 | 64 | 'libxslt1-dev', | ||
2713 | 65 | 'python-dev', | ||
2714 | 66 | 'python-pip', | ||
2715 | 67 | 'python-setuptools', | ||
2716 | 68 | 'zlib1g-dev', | ||
2717 | 69 | ] | ||
2718 | 70 | |||
2719 | 49 | SERVICES = [ | 71 | SERVICES = [ |
2721 | 50 | "glance-api", "glance-registry", ] | 72 | "glance-api", |
2722 | 73 | "glance-registry", | ||
2723 | 74 | ] | ||
2724 | 75 | |||
2725 | 76 | # ubuntu packages that should not be installed when deploying from git | ||
2726 | 77 | GIT_PACKAGE_BLACKLIST = [ | ||
2727 | 78 | 'glance', | ||
2728 | 79 | 'python-swiftclient', | ||
2729 | 80 | 'python-keystone', | ||
2730 | 81 | ] | ||
2731 | 82 | |||
2732 | 51 | 83 | ||
2733 | 52 | CHARM = "glance" | 84 | CHARM = "glance" |
2734 | 53 | 85 | ||
2735 | @@ -76,7 +108,9 @@ | |||
2736 | 76 | (GLANCE_REGISTRY_CONF, { | 108 | (GLANCE_REGISTRY_CONF, { |
2737 | 77 | 'hook_contexts': [context.SharedDBContext(ssl_dir=GLANCE_CONF_DIR), | 109 | 'hook_contexts': [context.SharedDBContext(ssl_dir=GLANCE_CONF_DIR), |
2738 | 78 | context.PostgresqlDBContext(), | 110 | context.PostgresqlDBContext(), |
2740 | 79 | context.IdentityServiceContext(), | 111 | context.IdentityServiceContext( |
2741 | 112 | service='glance', | ||
2742 | 113 | service_user='glance'), | ||
2743 | 80 | context.SyslogContext(), | 114 | context.SyslogContext(), |
2744 | 81 | glance_contexts.LoggingConfigContext(), | 115 | glance_contexts.LoggingConfigContext(), |
2745 | 82 | glance_contexts.GlanceIPv6Context(), | 116 | glance_contexts.GlanceIPv6Context(), |
2746 | @@ -90,7 +124,9 @@ | |||
2747 | 90 | 'hook_contexts': [context.SharedDBContext(ssl_dir=GLANCE_CONF_DIR), | 124 | 'hook_contexts': [context.SharedDBContext(ssl_dir=GLANCE_CONF_DIR), |
2748 | 91 | context.AMQPContext(ssl_dir=GLANCE_CONF_DIR), | 125 | context.AMQPContext(ssl_dir=GLANCE_CONF_DIR), |
2749 | 92 | context.PostgresqlDBContext(), | 126 | context.PostgresqlDBContext(), |
2751 | 93 | context.IdentityServiceContext(), | 127 | context.IdentityServiceContext( |
2752 | 128 | service='glance', | ||
2753 | 129 | service_user='glance'), | ||
2754 | 94 | glance_contexts.CephGlanceContext(), | 130 | glance_contexts.CephGlanceContext(), |
2755 | 95 | glance_contexts.ObjectStoreContext(), | 131 | glance_contexts.ObjectStoreContext(), |
2756 | 96 | glance_contexts.HAProxyContext(), | 132 | glance_contexts.HAProxyContext(), |
2757 | @@ -136,7 +172,7 @@ | |||
2758 | 136 | # Register config files with their respective contexts. | 172 | # Register config files with their respective contexts. |
2759 | 137 | # Regstration of some configs may not be required depending on | 173 | # Regstration of some configs may not be required depending on |
2760 | 138 | # existing of certain relations. | 174 | # existing of certain relations. |
2762 | 139 | release = get_os_codename_package('glance-common', fatal=False) or 'essex' | 175 | release = os_release('glance-common') |
2763 | 140 | configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, | 176 | configs = templating.OSConfigRenderer(templates_dir=TEMPLATES, |
2764 | 141 | openstack_release=release) | 177 | openstack_release=release) |
2765 | 142 | 178 | ||
2766 | @@ -173,6 +209,18 @@ | |||
2767 | 173 | return configs | 209 | return configs |
2768 | 174 | 210 | ||
2769 | 175 | 211 | ||
2770 | 212 | def determine_packages(): | ||
2771 | 213 | packages = [] + PACKAGES | ||
2772 | 214 | |||
2773 | 215 | if git_install_requested(): | ||
2774 | 216 | packages.extend(BASE_GIT_PACKAGES) | ||
2775 | 217 | # don't include packages that will be installed from git | ||
2776 | 218 | for p in GIT_PACKAGE_BLACKLIST: | ||
2777 | 219 | packages.remove(p) | ||
2778 | 220 | |||
2779 | 221 | return list(set(packages)) | ||
2780 | 222 | |||
2781 | 223 | |||
2782 | 176 | def migrate_database(): | 224 | def migrate_database(): |
2783 | 177 | '''Runs glance-manage to initialize a new database | 225 | '''Runs glance-manage to initialize a new database |
2784 | 178 | or migrate existing | 226 | or migrate existing |
2785 | @@ -201,7 +249,7 @@ | |||
2786 | 201 | ] | 249 | ] |
2787 | 202 | apt_update() | 250 | apt_update() |
2788 | 203 | apt_upgrade(options=dpkg_opts, fatal=True, dist=True) | 251 | apt_upgrade(options=dpkg_opts, fatal=True, dist=True) |
2790 | 204 | apt_install(PACKAGES, fatal=True) | 252 | apt_install(determine_packages(), fatal=True) |
2791 | 205 | 253 | ||
2792 | 206 | # set CONFIGS to load templates from new release and regenerate config | 254 | # set CONFIGS to load templates from new release and regenerate config |
2793 | 207 | configs.set_release(openstack_release=new_os_rel) | 255 | configs.set_release(openstack_release=new_os_rel) |
2794 | @@ -252,3 +300,85 @@ | |||
2795 | 252 | ' main') | 300 | ' main') |
2796 | 253 | apt_update() | 301 | apt_update() |
2797 | 254 | apt_install('haproxy/trusty-backports', fatal=True) | 302 | apt_install('haproxy/trusty-backports', fatal=True) |
2798 | 303 | |||
2799 | 304 | |||
2800 | 305 | def git_install(projects_yaml): | ||
2801 | 306 | """Perform setup, and install git repos specified in yaml parameter.""" | ||
2802 | 307 | if git_install_requested(): | ||
2803 | 308 | git_pre_install() | ||
2804 | 309 | git_clone_and_install(projects_yaml, core_project='glance') | ||
2805 | 310 | git_post_install(projects_yaml) | ||
2806 | 311 | |||
2807 | 312 | |||
2808 | 313 | def git_pre_install(): | ||
2809 | 314 | """Perform glance pre-install setup.""" | ||
2810 | 315 | dirs = [ | ||
2811 | 316 | '/var/lib/glance', | ||
2812 | 317 | '/var/lib/glance/images', | ||
2813 | 318 | '/var/lib/glance/image-cache', | ||
2814 | 319 | '/var/lib/glance/image-cache/incomplete', | ||
2815 | 320 | '/var/lib/glance/image-cache/invalid', | ||
2816 | 321 | '/var/lib/glance/image-cache/queue', | ||
2817 | 322 | '/var/log/glance', | ||
2818 | 323 | ] | ||
2819 | 324 | |||
2820 | 325 | logs = [ | ||
2821 | 326 | '/var/log/glance/glance-api.log', | ||
2822 | 327 | '/var/log/glance/glance-registry.log', | ||
2823 | 328 | ] | ||
2824 | 329 | |||
2825 | 330 | adduser('glance', shell='/bin/bash', system_user=True) | ||
2826 | 331 | add_group('glance', system_group=True) | ||
2827 | 332 | add_user_to_group('glance', 'glance') | ||
2828 | 333 | |||
2829 | 334 | for d in dirs: | ||
2830 | 335 | mkdir(d, owner='glance', group='glance', perms=0700, force=False) | ||
2831 | 336 | |||
2832 | 337 | for l in logs: | ||
2833 | 338 | write_file(l, '', owner='glance', group='glance', perms=0600) | ||
2834 | 339 | |||
2835 | 340 | |||
2836 | 341 | def git_post_install(projects_yaml): | ||
2837 | 342 | """Perform glance post-install setup.""" | ||
2838 | 343 | src_etc = os.path.join(git_src_dir(projects_yaml, 'glance'), 'etc') | ||
2839 | 344 | configs = { | ||
2840 | 345 | 'src': src_etc, | ||
2841 | 346 | 'dest': '/etc/glance', | ||
2842 | 347 | } | ||
2843 | 348 | |||
2844 | 349 | if os.path.exists(configs['dest']): | ||
2845 | 350 | shutil.rmtree(configs['dest']) | ||
2846 | 351 | shutil.copytree(configs['src'], configs['dest']) | ||
2847 | 352 | |||
2848 | 353 | glance_api_context = { | ||
2849 | 354 | 'service_description': 'Glance API server', | ||
2850 | 355 | 'service_name': 'Glance', | ||
2851 | 356 | 'user_name': 'glance', | ||
2852 | 357 | 'start_dir': '/var/lib/glance', | ||
2853 | 358 | 'process_name': 'glance-api', | ||
2854 | 359 | 'executable_name': '/usr/local/bin/glance-api', | ||
2855 | 360 | 'config_files': ['/etc/glance/glance-api.conf'], | ||
2856 | 361 | 'log_file': '/var/log/glance/api.log', | ||
2857 | 362 | } | ||
2858 | 363 | |||
2859 | 364 | glance_registry_context = { | ||
2860 | 365 | 'service_description': 'Glance registry server', | ||
2861 | 366 | 'service_name': 'Glance', | ||
2862 | 367 | 'user_name': 'glance', | ||
2863 | 368 | 'start_dir': '/var/lib/glance', | ||
2864 | 369 | 'process_name': 'glance-registry', | ||
2865 | 370 | 'executable_name': '/usr/local/bin/glance-registry', | ||
2866 | 371 | 'config_files': ['/etc/glance/glance-registry.conf'], | ||
2867 | 372 | 'log_file': '/var/log/glance/registry.log', | ||
2868 | 373 | } | ||
2869 | 374 | |||
2870 | 375 | # NOTE(coreycb): Needs systemd support | ||
2871 | 376 | templates_dir = 'hooks/charmhelpers/contrib/openstack/templates' | ||
2872 | 377 | templates_dir = os.path.join(charm_dir(), templates_dir) | ||
2873 | 378 | render('git.upstart', '/etc/init/glance-api.conf', | ||
2874 | 379 | glance_api_context, perms=0o644, templates_dir=templates_dir) | ||
2875 | 380 | render('git.upstart', '/etc/init/glance-registry.conf', | ||
2876 | 381 | glance_registry_context, perms=0o644, templates_dir=templates_dir) | ||
2877 | 382 | |||
2878 | 383 | service_restart('glance-api') | ||
2879 | 384 | service_restart('glance-registry') | ||
2880 | 255 | 385 | ||
2881 | === added directory 'templates/kilo' | |||
2882 | === added file 'templates/kilo/glance-api-paste.ini' | |||
2883 | --- templates/kilo/glance-api-paste.ini 1970-01-01 00:00:00 +0000 | |||
2884 | +++ templates/kilo/glance-api-paste.ini 2015-04-16 21:50:07 +0000 | |||
2885 | @@ -0,0 +1,77 @@ | |||
2886 | 1 | # Use this pipeline for no auth or image caching - DEFAULT | ||
2887 | 2 | [pipeline:glance-api] | ||
2888 | 3 | pipeline = versionnegotiation osprofiler unauthenticated-context rootapp | ||
2889 | 4 | |||
2890 | 5 | # Use this pipeline for image caching and no auth | ||
2891 | 6 | [pipeline:glance-api-caching] | ||
2892 | 7 | pipeline = versionnegotiation osprofiler unauthenticated-context cache rootapp | ||
2893 | 8 | |||
2894 | 9 | # Use this pipeline for caching w/ management interface but no auth | ||
2895 | 10 | [pipeline:glance-api-cachemanagement] | ||
2896 | 11 | pipeline = versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp | ||
2897 | 12 | |||
2898 | 13 | # Use this pipeline for keystone auth | ||
2899 | 14 | [pipeline:glance-api-keystone] | ||
2900 | 15 | pipeline = versionnegotiation osprofiler authtoken context rootapp | ||
2901 | 16 | |||
2902 | 17 | # Use this pipeline for keystone auth with image caching | ||
2903 | 18 | [pipeline:glance-api-keystone+caching] | ||
2904 | 19 | pipeline = versionnegotiation osprofiler authtoken context cache rootapp | ||
2905 | 20 | |||
2906 | 21 | # Use this pipeline for keystone auth with caching and cache management | ||
2907 | 22 | [pipeline:glance-api-keystone+cachemanagement] | ||
2908 | 23 | pipeline = versionnegotiation osprofiler authtoken context cache cachemanage rootapp | ||
2909 | 24 | |||
2910 | 25 | # Use this pipeline for authZ only. This means that the registry will treat a | ||
2911 | 26 | # user as authenticated without making requests to keystone to reauthenticate | ||
2912 | 27 | # the user. | ||
2913 | 28 | [pipeline:glance-api-trusted-auth] | ||
2914 | 29 | pipeline = versionnegotiation osprofiler context rootapp | ||
2915 | 30 | |||
2916 | 31 | # Use this pipeline for authZ only. This means that the registry will treat a | ||
2917 | 32 | # user as authenticated without making requests to keystone to reauthenticate | ||
2918 | 33 | # the user and uses cache management | ||
2919 | 34 | [pipeline:glance-api-trusted-auth+cachemanagement] | ||
2920 | 35 | pipeline = versionnegotiation osprofiler context cache cachemanage rootapp | ||
2921 | 36 | |||
2922 | 37 | [composite:rootapp] | ||
2923 | 38 | paste.composite_factory = glance.api:root_app_factory | ||
2924 | 39 | /: apiversions | ||
2925 | 40 | /v1: apiv1app | ||
2926 | 41 | /v2: apiv2app | ||
2927 | 42 | |||
2928 | 43 | [app:apiversions] | ||
2929 | 44 | paste.app_factory = glance.api.versions:create_resource | ||
2930 | 45 | |||
2931 | 46 | [app:apiv1app] | ||
2932 | 47 | paste.app_factory = glance.api.v1.router:API.factory | ||
2933 | 48 | |||
2934 | 49 | [app:apiv2app] | ||
2935 | 50 | paste.app_factory = glance.api.v2.router:API.factory | ||
2936 | 51 | |||
2937 | 52 | [filter:versionnegotiation] | ||
2938 | 53 | paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory | ||
2939 | 54 | |||
2940 | 55 | [filter:cache] | ||
2941 | 56 | paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory | ||
2942 | 57 | |||
2943 | 58 | [filter:cachemanage] | ||
2944 | 59 | paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory | ||
2945 | 60 | |||
2946 | 61 | [filter:context] | ||
2947 | 62 | paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory | ||
2948 | 63 | |||
2949 | 64 | [filter:unauthenticated-context] | ||
2950 | 65 | paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory | ||
2951 | 66 | |||
2952 | 67 | [filter:authtoken] | ||
2953 | 68 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory | ||
2954 | 69 | delay_auth_decision = true | ||
2955 | 70 | |||
2956 | 71 | [filter:gzip] | ||
2957 | 72 | paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory | ||
2958 | 73 | |||
2959 | 74 | [filter:osprofiler] | ||
2960 | 75 | paste.filter_factory = osprofiler.web:WsgiMiddleware.factory | ||
2961 | 76 | hmac_keys = SECRET_KEY | ||
2962 | 77 | enabled = yes | ||
2963 | 0 | 78 | ||
2964 | === added file 'templates/kilo/glance-api.conf' | |||
2965 | --- templates/kilo/glance-api.conf 1970-01-01 00:00:00 +0000 | |||
2966 | +++ templates/kilo/glance-api.conf 2015-04-16 21:50:07 +0000 | |||
2967 | @@ -0,0 +1,83 @@ | |||
2968 | 1 | [DEFAULT] | ||
2969 | 2 | verbose = {{ verbose }} | ||
2970 | 3 | use_syslog = {{ use_syslog }} | ||
2971 | 4 | debug = {{ debug }} | ||
2972 | 5 | workers = {{ workers }} | ||
2973 | 6 | |||
2974 | 7 | known_stores = {{ known_stores }} | ||
2975 | 8 | {% if rbd_pool -%} | ||
2976 | 9 | default_store = rbd | ||
2977 | 10 | {% elif swift_store -%} | ||
2978 | 11 | default_store = swift | ||
2979 | 12 | {% else -%} | ||
2980 | 13 | default_store = file | ||
2981 | 14 | {% endif -%} | ||
2982 | 15 | |||
2983 | 16 | bind_host = {{ bind_host }} | ||
2984 | 17 | |||
2985 | 18 | {% if ext -%} | ||
2986 | 19 | bind_port = {{ ext }} | ||
2987 | 20 | {% elif bind_port -%} | ||
2988 | 21 | bind_port = {{ bind_port }} | ||
2989 | 22 | {% else -%} | ||
2990 | 23 | bind_port = 9292 | ||
2991 | 24 | {% endif -%} | ||
2992 | 25 | |||
2993 | 26 | log_file = /var/log/glance/api.log | ||
2994 | 27 | backlog = 4096 | ||
2995 | 28 | |||
2996 | 29 | registry_host = {{ registry_host }} | ||
2997 | 30 | registry_port = 9191 | ||
2998 | 31 | registry_client_protocol = http | ||
2999 | 32 | |||
3000 | 33 | {% if api_config_flags -%} | ||
3001 | 34 | {% for key, value in api_config_flags.iteritems() -%} | ||
3002 | 35 | {{ key }} = {{ value }} | ||
3003 | 36 | {% endfor -%} | ||
3004 | 37 | {% endif -%} | ||
3005 | 38 | |||
3006 | 39 | {% if rabbitmq_host or rabbitmq_hosts -%} | ||
3007 | 40 | notification_driver = rabbit | ||
3008 | 41 | {% endif -%} | ||
3009 | 42 | |||
3010 | 43 | {% if swift_store -%} | ||
3011 | 44 | swift_store_auth_version = 2 | ||
3012 | 45 | swift_store_auth_address = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v2.0/ | ||
3013 | 46 | swift_store_user = {{ admin_tenant_name }}:{{ admin_user }} | ||
3014 | 47 | swift_store_key = {{ admin_password }} | ||
3015 | 48 | swift_store_create_container_on_put = True | ||
3016 | 49 | swift_store_container = glance | ||
3017 | 50 | swift_store_large_object_size = 5120 | ||
3018 | 51 | swift_store_large_object_chunk_size = 200 | ||
3019 | 52 | swift_enable_snet = False | ||
3020 | 53 | {% endif -%} | ||
3021 | 54 | |||
3022 | 55 | {% if rbd_pool -%} | ||
3023 | 56 | rbd_store_ceph_conf = /etc/ceph/ceph.conf | ||
3024 | 57 | rbd_store_user = {{ rbd_user }} | ||
3025 | 58 | rbd_store_pool = {{ rbd_pool }} | ||
3026 | 59 | rbd_store_chunk_size = 8 | ||
3027 | 60 | {% endif -%} | ||
3028 | 61 | |||
3029 | 62 | delayed_delete = False | ||
3030 | 63 | scrub_time = 43200 | ||
3031 | 64 | scrubber_datadir = /var/lib/glance/scrubber | ||
3032 | 65 | image_cache_dir = /var/lib/glance/image-cache/ | ||
3033 | 66 | db_enforce_mysql_charset = False | ||
3034 | 67 | |||
3035 | 68 | [glance_store] | ||
3036 | 69 | filesystem_store_datadir = /var/lib/glance/images/ | ||
3037 | 70 | |||
3038 | 71 | [image_format] | ||
3039 | 72 | disk_formats=ami,ari,aki,vhd,vmdk,raw,qcow2,vdi,iso,root-tar | ||
3040 | 73 | |||
3041 | 74 | {% include "section-keystone-authtoken" %} | ||
3042 | 75 | |||
3043 | 76 | {% if auth_host -%} | ||
3044 | 77 | [paste_deploy] | ||
3045 | 78 | flavor = keystone | ||
3046 | 79 | {% endif %} | ||
3047 | 80 | |||
3048 | 81 | {% include "parts/section-database" %} | ||
3049 | 82 | |||
3050 | 83 | {% include "section-rabbitmq-oslo" %} | ||
3051 | 0 | 84 | ||
3052 | === added file 'templates/kilo/glance-registry-paste.ini' | |||
3053 | --- templates/kilo/glance-registry-paste.ini 1970-01-01 00:00:00 +0000 | |||
3054 | +++ templates/kilo/glance-registry-paste.ini 2015-04-16 21:50:07 +0000 | |||
3055 | @@ -0,0 +1,30 @@ | |||
3056 | 1 | # Use this pipeline for no auth - DEFAULT | ||
3057 | 2 | [pipeline:glance-registry] | ||
3058 | 3 | pipeline = osprofiler unauthenticated-context registryapp | ||
3059 | 4 | |||
3060 | 5 | # Use this pipeline for keystone auth | ||
3061 | 6 | [pipeline:glance-registry-keystone] | ||
3062 | 7 | pipeline = osprofiler authtoken context registryapp | ||
3063 | 8 | |||
3064 | 9 | # Use this pipeline for authZ only. This means that the registry will treat a | ||
3065 | 10 | # user as authenticated without making requests to keystone to reauthenticate | ||
3066 | 11 | # the user. | ||
3067 | 12 | [pipeline:glance-registry-trusted-auth] | ||
3068 | 13 | pipeline = osprofiler context registryapp | ||
3069 | 14 | |||
3070 | 15 | [app:registryapp] | ||
3071 | 16 | paste.app_factory = glance.registry.api:API.factory | ||
3072 | 17 | |||
3073 | 18 | [filter:context] | ||
3074 | 19 | paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory | ||
3075 | 20 | |||
3076 | 21 | [filter:unauthenticated-context] | ||
3077 | 22 | paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory | ||
3078 | 23 | |||
3079 | 24 | [filter:authtoken] | ||
3080 | 25 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory | ||
3081 | 26 | |||
3082 | 27 | [filter:osprofiler] | ||
3083 | 28 | paste.filter_factory = osprofiler.web:WsgiMiddleware.factory | ||
3084 | 29 | hmac_keys = SECRET_KEY | ||
3085 | 30 | enabled = yes | ||
3086 | 0 | 31 | ||
3087 | === added file 'templates/kilo/glance-registry.conf' | |||
3088 | --- templates/kilo/glance-registry.conf 1970-01-01 00:00:00 +0000 | |||
3089 | +++ templates/kilo/glance-registry.conf 2015-04-16 21:50:07 +0000 | |||
3090 | @@ -0,0 +1,27 @@ | |||
3091 | 1 | [DEFAULT] | ||
3092 | 2 | verbose = {{ verbose }} | ||
3093 | 3 | use_syslog = {{ use_syslog }} | ||
3094 | 4 | debug = {{ debug }} | ||
3095 | 5 | workers = {{ workers }} | ||
3096 | 6 | |||
3097 | 7 | bind_host = {{ bind_host }} | ||
3098 | 8 | bind_port = 9191 | ||
3099 | 9 | log_file = /var/log/glance/registry.log | ||
3100 | 10 | backlog = 4096 | ||
3101 | 11 | api_limit_max = 1000 | ||
3102 | 12 | limit_param_default = 25 | ||
3103 | 13 | |||
3104 | 14 | {% if registry_config_flags -%} | ||
3105 | 15 | {% for key, value in registry_config_flags.iteritems() -%} | ||
3106 | 16 | {{ key }} = {{ value }} | ||
3107 | 17 | {% endfor -%} | ||
3108 | 18 | {% endif -%} | ||
3109 | 19 | |||
3110 | 20 | {% include "section-keystone-authtoken" %} | ||
3111 | 21 | |||
3112 | 22 | {% if auth_host -%} | ||
3113 | 23 | [paste_deploy] | ||
3114 | 24 | flavor = keystone | ||
3115 | 25 | {% endif %} | ||
3116 | 26 | |||
3117 | 27 | {% include "parts/section-database" %} | ||
3118 | 0 | 28 | ||
3119 | === modified file 'templates/parts/keystone' | |||
3120 | --- templates/parts/keystone 2014-04-12 16:16:54 +0000 | |||
3121 | +++ templates/parts/keystone 2015-04-16 21:50:07 +0000 | |||
3122 | @@ -7,6 +7,7 @@ | |||
3123 | 7 | admin_tenant_name = {{ admin_tenant_name }} | 7 | admin_tenant_name = {{ admin_tenant_name }} |
3124 | 8 | admin_user = {{ admin_user }} | 8 | admin_user = {{ admin_user }} |
3125 | 9 | admin_password = {{ admin_password }} | 9 | admin_password = {{ admin_password }} |
3126 | 10 | signing_dir = {{ signing_dir }} | ||
3127 | 10 | 11 | ||
3128 | 11 | [paste_deploy] | 12 | [paste_deploy] |
3129 | 12 | flavor = keystone | 13 | flavor = keystone |
3130 | 13 | 14 | ||
3131 | === modified file 'templates/parts/section-database' | |||
3132 | --- templates/parts/section-database 2014-04-12 15:29:10 +0000 | |||
3133 | +++ templates/parts/section-database 2015-04-16 21:50:07 +0000 | |||
3134 | @@ -1,4 +1,5 @@ | |||
3135 | 1 | {% if database_host -%} | 1 | {% if database_host -%} |
3136 | 2 | [database] | 2 | [database] |
3137 | 3 | connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} | 3 | connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} |
3138 | 4 | idle_timeout = 3600 | ||
3139 | 4 | {% endif -%} | 5 | {% endif -%} |
3140 | 5 | 6 | ||
3141 | === renamed file 'tests/14-basic-precise-icehouse' => 'tests/014-basic-precise-icehouse' | |||
3142 | === renamed file 'tests/15-basic-trusty-icehouse' => 'tests/015-basic-trusty-icehouse' | |||
3143 | === added file 'tests/016-basic-trusty-juno' | |||
3144 | --- tests/016-basic-trusty-juno 1970-01-01 00:00:00 +0000 | |||
3145 | +++ tests/016-basic-trusty-juno 2015-04-16 21:50:07 +0000 | |||
3146 | @@ -0,0 +1,11 @@ | |||
3147 | 1 | #!/usr/bin/python | ||
3148 | 2 | |||
3149 | 3 | """Amulet tests on a basic Glance deployment on trusty-juno.""" | ||
3150 | 4 | |||
3151 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3152 | 6 | |||
3153 | 7 | if __name__ == '__main__': | ||
3154 | 8 | deployment = GlanceBasicDeployment(series='trusty', | ||
3155 | 9 | openstack='cloud:trusty-juno', | ||
3156 | 10 | source='cloud:trusty-updates/juno') | ||
3157 | 11 | deployment.run_tests() | ||
3158 | 0 | 12 | ||
3159 | === added file 'tests/017-basic-trusty-kilo' | |||
3160 | --- tests/017-basic-trusty-kilo 1970-01-01 00:00:00 +0000 | |||
3161 | +++ tests/017-basic-trusty-kilo 2015-04-16 21:50:07 +0000 | |||
3162 | @@ -0,0 +1,11 @@ | |||
3163 | 1 | #!/usr/bin/python | ||
3164 | 2 | |||
3165 | 3 | """Amulet tests on a basic glance deployment on trusty-kilo.""" | ||
3166 | 4 | |||
3167 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3168 | 6 | |||
3169 | 7 | if __name__ == '__main__': | ||
3170 | 8 | deployment = GlanceBasicDeployment(series='trusty', | ||
3171 | 9 | openstack='cloud:trusty-kilo', | ||
3172 | 10 | source='cloud:trusty-updates/kilo') | ||
3173 | 11 | deployment.run_tests() | ||
3174 | 0 | 12 | ||
3175 | === added file 'tests/018-basic-utopic-juno' | |||
3176 | --- tests/018-basic-utopic-juno 1970-01-01 00:00:00 +0000 | |||
3177 | +++ tests/018-basic-utopic-juno 2015-04-16 21:50:07 +0000 | |||
3178 | @@ -0,0 +1,9 @@ | |||
3179 | 1 | #!/usr/bin/python | ||
3180 | 2 | |||
3181 | 3 | """Amulet tests on a basic Glance deployment on utopic-juno.""" | ||
3182 | 4 | |||
3183 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3184 | 6 | |||
3185 | 7 | if __name__ == '__main__': | ||
3186 | 8 | deployment = GlanceBasicDeployment(series='utopic') | ||
3187 | 9 | deployment.run_tests() | ||
3188 | 0 | 10 | ||
3189 | === added file 'tests/019-basic-vivid-kilo' | |||
3190 | --- tests/019-basic-vivid-kilo 1970-01-01 00:00:00 +0000 | |||
3191 | +++ tests/019-basic-vivid-kilo 2015-04-16 21:50:07 +0000 | |||
3192 | @@ -0,0 +1,9 @@ | |||
3193 | 1 | #!/usr/bin/python | ||
3194 | 2 | |||
3195 | 3 | """Amulet tests on a basic Glance deployment on vivid-kilo.""" | ||
3196 | 4 | |||
3197 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3198 | 6 | |||
3199 | 7 | if __name__ == '__main__': | ||
3200 | 8 | deployment = GlanceBasicDeployment(series='vivid') | ||
3201 | 9 | deployment.run_tests() | ||
3202 | 0 | 10 | ||
3203 | === added file 'tests/050-basic-trusty-icehouse-git' | |||
3204 | --- tests/050-basic-trusty-icehouse-git 1970-01-01 00:00:00 +0000 | |||
3205 | +++ tests/050-basic-trusty-icehouse-git 2015-04-16 21:50:07 +0000 | |||
3206 | @@ -0,0 +1,9 @@ | |||
3207 | 1 | #!/usr/bin/python | ||
3208 | 2 | |||
3209 | 3 | """Amulet tests on a basic Glance git deployment on trusty-icehouse.""" | ||
3210 | 4 | |||
3211 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3212 | 6 | |||
3213 | 7 | if __name__ == '__main__': | ||
3214 | 8 | deployment = GlanceBasicDeployment(series='trusty', git=True) | ||
3215 | 9 | deployment.run_tests() | ||
3216 | 0 | 10 | ||
3217 | === added file 'tests/051-basic-trusty-juno-git' | |||
3218 | --- tests/051-basic-trusty-juno-git 1970-01-01 00:00:00 +0000 | |||
3219 | +++ tests/051-basic-trusty-juno-git 2015-04-16 21:50:07 +0000 | |||
3220 | @@ -0,0 +1,12 @@ | |||
3221 | 1 | #!/usr/bin/python | ||
3222 | 2 | |||
3223 | 3 | """Amulet tests on a basic Glance git deployment on trusty-juno.""" | ||
3224 | 4 | |||
3225 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3226 | 6 | |||
3227 | 7 | if __name__ == '__main__': | ||
3228 | 8 | deployment = GlanceBasicDeployment(series='trusty', | ||
3229 | 9 | openstack='cloud:trusty-juno', | ||
3230 | 10 | source='cloud:trusty-updates/juno', | ||
3231 | 11 | git=True) | ||
3232 | 12 | deployment.run_tests() | ||
3233 | 0 | 13 | ||
3234 | === removed file 'tests/10-basic-precise-essex' | |||
3235 | --- tests/10-basic-precise-essex 2014-07-11 14:11:03 +0000 | |||
3236 | +++ tests/10-basic-precise-essex 1970-01-01 00:00:00 +0000 | |||
3237 | @@ -1,9 +0,0 @@ | |||
3238 | 1 | #!/usr/bin/python | ||
3239 | 2 | |||
3240 | 3 | """Amulet tests on a basic glance deployment on precise-essex.""" | ||
3241 | 4 | |||
3242 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3243 | 6 | |||
3244 | 7 | if __name__ == '__main__': | ||
3245 | 8 | deployment = GlanceBasicDeployment(series='precise') | ||
3246 | 9 | deployment.run_tests() | ||
3247 | 10 | 0 | ||
3248 | === removed file 'tests/11-basic-precise-folsom' | |||
3249 | --- tests/11-basic-precise-folsom 2014-07-11 14:11:03 +0000 | |||
3250 | +++ tests/11-basic-precise-folsom 1970-01-01 00:00:00 +0000 | |||
3251 | @@ -1,11 +0,0 @@ | |||
3252 | 1 | #!/usr/bin/python | ||
3253 | 2 | |||
3254 | 3 | """Amulet tests on a basic glance deployment on precise-folsom.""" | ||
3255 | 4 | |||
3256 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3257 | 6 | |||
3258 | 7 | if __name__ == '__main__': | ||
3259 | 8 | deployment = GlanceBasicDeployment(series='precise', | ||
3260 | 9 | openstack='cloud:precise-folsom', | ||
3261 | 10 | source='cloud:precise-updates/folsom') | ||
3262 | 11 | deployment.run_tests() | ||
3263 | 12 | 0 | ||
3264 | === removed file 'tests/12-basic-precise-grizzly' | |||
3265 | --- tests/12-basic-precise-grizzly 2014-07-11 14:11:03 +0000 | |||
3266 | +++ tests/12-basic-precise-grizzly 1970-01-01 00:00:00 +0000 | |||
3267 | @@ -1,11 +0,0 @@ | |||
3268 | 1 | #!/usr/bin/python | ||
3269 | 2 | |||
3270 | 3 | """Amulet tests on a basic glance deployment on precise-grizzly.""" | ||
3271 | 4 | |||
3272 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3273 | 6 | |||
3274 | 7 | if __name__ == '__main__': | ||
3275 | 8 | deployment = GlanceBasicDeployment(series='precise', | ||
3276 | 9 | openstack='cloud:precise-grizzly', | ||
3277 | 10 | source='cloud:precise-updates/grizzly') | ||
3278 | 11 | deployment.run_tests() | ||
3279 | 12 | 0 | ||
3280 | === removed file 'tests/13-basic-precise-havana' | |||
3281 | --- tests/13-basic-precise-havana 2014-07-11 14:11:03 +0000 | |||
3282 | +++ tests/13-basic-precise-havana 1970-01-01 00:00:00 +0000 | |||
3283 | @@ -1,11 +0,0 @@ | |||
3284 | 1 | #!/usr/bin/python | ||
3285 | 2 | |||
3286 | 3 | """Amulet tests on a basic glance deployment on precise-havana.""" | ||
3287 | 4 | |||
3288 | 5 | from basic_deployment import GlanceBasicDeployment | ||
3289 | 6 | |||
3290 | 7 | if __name__ == '__main__': | ||
3291 | 8 | deployment = GlanceBasicDeployment(series='precise', | ||
3292 | 9 | openstack='cloud:precise-havana', | ||
3293 | 10 | source='cloud:precise-updates/havana') | ||
3294 | 11 | deployment.run_tests() | ||
3295 | 12 | 0 | ||
3296 | === modified file 'tests/basic_deployment.py' (properties changed: +x to -x) | |||
3297 | --- tests/basic_deployment.py 2015-04-09 00:37:53 +0000 | |||
3298 | +++ tests/basic_deployment.py 2015-04-16 21:50:07 +0000 | |||
3299 | @@ -1,6 +1,8 @@ | |||
3300 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
3301 | 2 | 2 | ||
3302 | 3 | import amulet | 3 | import amulet |
3303 | 4 | import os | ||
3304 | 5 | import yaml | ||
3305 | 4 | 6 | ||
3306 | 5 | from charmhelpers.contrib.openstack.amulet.deployment import ( | 7 | from charmhelpers.contrib.openstack.amulet.deployment import ( |
3307 | 6 | OpenStackAmuletDeployment | 8 | OpenStackAmuletDeployment |
3308 | @@ -13,7 +15,7 @@ | |||
3309 | 13 | ) | 15 | ) |
3310 | 14 | 16 | ||
3311 | 15 | # Use DEBUG to turn on debug logging | 17 | # Use DEBUG to turn on debug logging |
3313 | 16 | u = OpenStackAmuletUtils(ERROR) | 18 | u = OpenStackAmuletUtils(DEBUG) |
3314 | 17 | 19 | ||
3315 | 18 | class GlanceBasicDeployment(OpenStackAmuletDeployment): | 20 | class GlanceBasicDeployment(OpenStackAmuletDeployment): |
3316 | 19 | '''Amulet tests on a basic file-backed glance deployment. Verify relations, | 21 | '''Amulet tests on a basic file-backed glance deployment. Verify relations, |
3317 | @@ -23,9 +25,11 @@ | |||
3318 | 23 | # * Add tests with different storage back ends | 25 | # * Add tests with different storage back ends |
3319 | 24 | # * Resolve Essex->Havana juju set charm bug | 26 | # * Resolve Essex->Havana juju set charm bug |
3320 | 25 | 27 | ||
3322 | 26 | def __init__(self, series=None, openstack=None, source=None, stable=True): | 28 | def __init__(self, series=None, openstack=None, source=None, git=False, |
3323 | 29 | stable=False): | ||
3324 | 27 | '''Deploy the entire test environment.''' | 30 | '''Deploy the entire test environment.''' |
3325 | 28 | super(GlanceBasicDeployment, self).__init__(series, openstack, source, stable) | 31 | super(GlanceBasicDeployment, self).__init__(series, openstack, source, stable) |
3326 | 32 | self.git = git | ||
3327 | 29 | self._add_services() | 33 | self._add_services() |
3328 | 30 | self._add_relations() | 34 | self._add_relations() |
3329 | 31 | self._configure_services() | 35 | self._configure_services() |
3330 | @@ -55,11 +59,30 @@ | |||
3331 | 55 | 59 | ||
3332 | 56 | def _configure_services(self): | 60 | def _configure_services(self): |
3333 | 57 | '''Configure all of the services.''' | 61 | '''Configure all of the services.''' |
3334 | 62 | glance_config = {} | ||
3335 | 63 | if self.git: | ||
3336 | 64 | branch = 'stable/' + self._get_openstack_release_string() | ||
3337 | 65 | amulet_http_proxy = os.environ.get('AMULET_HTTP_PROXY') | ||
3338 | 66 | openstack_origin_git = { | ||
3339 | 67 | 'repositories': [ | ||
3340 | 68 | {'name': 'requirements', | ||
3341 | 69 | 'repository': 'git://git.openstack.org/openstack/requirements', | ||
3342 | 70 | 'branch': branch}, | ||
3343 | 71 | {'name': 'glance', | ||
3344 | 72 | 'repository': 'git://git.openstack.org/openstack/glance', | ||
3345 | 73 | 'branch': branch}, | ||
3346 | 74 | ], | ||
3347 | 75 | 'directory': '/mnt/openstack-git', | ||
3348 | 76 | 'http_proxy': amulet_http_proxy, | ||
3349 | 77 | 'https_proxy': amulet_http_proxy, | ||
3350 | 78 | } | ||
3351 | 79 | glance_config['openstack-origin-git'] = yaml.dump(openstack_origin_git) | ||
3352 | 80 | |||
3353 | 58 | keystone_config = {'admin-password': 'openstack', | 81 | keystone_config = {'admin-password': 'openstack', |
3354 | 59 | 'admin-token': 'ubuntutesting'} | 82 | 'admin-token': 'ubuntutesting'} |
3355 | 60 | |||
3356 | 61 | mysql_config = {'dataset-size': '50%'} | 83 | mysql_config = {'dataset-size': '50%'} |
3358 | 62 | configs = {'keystone': keystone_config, | 84 | configs = {'glance': glance_config, |
3359 | 85 | 'keystone': keystone_config, | ||
3360 | 63 | 'mysql': mysql_config} | 86 | 'mysql': mysql_config} |
3361 | 64 | super(GlanceBasicDeployment, self)._configure_services(configs) | 87 | super(GlanceBasicDeployment, self)._configure_services(configs) |
3362 | 65 | 88 | ||
3363 | 66 | 89 | ||
3364 | === modified file 'tests/charmhelpers/contrib/amulet/utils.py' | |||
3365 | --- tests/charmhelpers/contrib/amulet/utils.py 2015-01-26 09:45:23 +0000 | |||
3366 | +++ tests/charmhelpers/contrib/amulet/utils.py 2015-04-16 21:50:07 +0000 | |||
3367 | @@ -118,6 +118,9 @@ | |||
3368 | 118 | longs, or can be a function that evaluate a variable and returns a | 118 | longs, or can be a function that evaluate a variable and returns a |
3369 | 119 | bool. | 119 | bool. |
3370 | 120 | """ | 120 | """ |
3371 | 121 | self.log.debug('actual: {}'.format(repr(actual))) | ||
3372 | 122 | self.log.debug('expected: {}'.format(repr(expected))) | ||
3373 | 123 | |||
3374 | 121 | for k, v in six.iteritems(expected): | 124 | for k, v in six.iteritems(expected): |
3375 | 122 | if k in actual: | 125 | if k in actual: |
3376 | 123 | if (isinstance(v, six.string_types) or | 126 | if (isinstance(v, six.string_types) or |
3377 | @@ -134,7 +137,6 @@ | |||
3378 | 134 | def validate_relation_data(self, sentry_unit, relation, expected): | 137 | def validate_relation_data(self, sentry_unit, relation, expected): |
3379 | 135 | """Validate actual relation data based on expected relation data.""" | 138 | """Validate actual relation data based on expected relation data.""" |
3380 | 136 | actual = sentry_unit.relation(relation[0], relation[1]) | 139 | actual = sentry_unit.relation(relation[0], relation[1]) |
3381 | 137 | self.log.debug('actual: {}'.format(repr(actual))) | ||
3382 | 138 | return self._validate_dict_data(expected, actual) | 140 | return self._validate_dict_data(expected, actual) |
3383 | 139 | 141 | ||
3384 | 140 | def _validate_list_data(self, expected, actual): | 142 | def _validate_list_data(self, expected, actual): |
3385 | @@ -169,8 +171,13 @@ | |||
3386 | 169 | cmd = 'pgrep -o -f {}'.format(service) | 171 | cmd = 'pgrep -o -f {}'.format(service) |
3387 | 170 | else: | 172 | else: |
3388 | 171 | cmd = 'pgrep -o {}'.format(service) | 173 | cmd = 'pgrep -o {}'.format(service) |
3391 | 172 | proc_dir = '/proc/{}'.format(sentry_unit.run(cmd)[0].strip()) | 174 | cmd = cmd + ' | grep -v pgrep || exit 0' |
3392 | 173 | return self._get_dir_mtime(sentry_unit, proc_dir) | 175 | cmd_out = sentry_unit.run(cmd) |
3393 | 176 | self.log.debug('CMDout: ' + str(cmd_out)) | ||
3394 | 177 | if cmd_out[0]: | ||
3395 | 178 | self.log.debug('Pid for %s %s' % (service, str(cmd_out[0]))) | ||
3396 | 179 | proc_dir = '/proc/{}'.format(cmd_out[0].strip()) | ||
3397 | 180 | return self._get_dir_mtime(sentry_unit, proc_dir) | ||
3398 | 174 | 181 | ||
3399 | 175 | def service_restarted(self, sentry_unit, service, filename, | 182 | def service_restarted(self, sentry_unit, service, filename, |
3400 | 176 | pgrep_full=False, sleep_time=20): | 183 | pgrep_full=False, sleep_time=20): |
3401 | @@ -187,6 +194,121 @@ | |||
3402 | 187 | else: | 194 | else: |
3403 | 188 | return False | 195 | return False |
3404 | 189 | 196 | ||
3405 | 197 | def service_restarted_since(self, sentry_unit, mtime, service, | ||
3406 | 198 | pgrep_full=False, sleep_time=20, | ||
3407 | 199 | retry_count=2): | ||
3408 | 200 | """Check if service was been started after a given time. | ||
3409 | 201 | |||
3410 | 202 | Args: | ||
3411 | 203 | sentry_unit (sentry): The sentry unit to check for the service on | ||
3412 | 204 | mtime (float): The epoch time to check against | ||
3413 | 205 | service (string): service name to look for in process table | ||
3414 | 206 | pgrep_full (boolean): Use full command line search mode with pgrep | ||
3415 | 207 | sleep_time (int): Seconds to sleep before looking for process | ||
3416 | 208 | retry_count (int): If service is not found, how many times to retry | ||
3417 | 209 | |||
3418 | 210 | Returns: | ||
3419 | 211 | bool: True if service found and its start time it newer than mtime, | ||
3420 | 212 | False if service is older than mtime or if service was | ||
3421 | 213 | not found. | ||
3422 | 214 | """ | ||
3423 | 215 | self.log.debug('Checking %s restarted since %s' % (service, mtime)) | ||
3424 | 216 | time.sleep(sleep_time) | ||
3425 | 217 | proc_start_time = self._get_proc_start_time(sentry_unit, service, | ||
3426 | 218 | pgrep_full) | ||
3427 | 219 | while retry_count > 0 and not proc_start_time: | ||
3428 | 220 | self.log.debug('No pid file found for service %s, will retry %i ' | ||
3429 | 221 | 'more times' % (service, retry_count)) | ||
3430 | 222 | time.sleep(30) | ||
3431 | 223 | proc_start_time = self._get_proc_start_time(sentry_unit, service, | ||
3432 | 224 | pgrep_full) | ||
3433 | 225 | retry_count = retry_count - 1 | ||
3434 | 226 | |||
3435 | 227 | if not proc_start_time: | ||
3436 | 228 | self.log.warn('No proc start time found, assuming service did ' | ||
3437 | 229 | 'not start') | ||
3438 | 230 | return False | ||
3439 | 231 | if proc_start_time >= mtime: | ||
3440 | 232 | self.log.debug('proc start time is newer than provided mtime' | ||
3441 | 233 | '(%s >= %s)' % (proc_start_time, mtime)) | ||
3442 | 234 | return True | ||
3443 | 235 | else: | ||
3444 | 236 | self.log.warn('proc start time (%s) is older than provided mtime ' | ||
3445 | 237 | '(%s), service did not restart' % (proc_start_time, | ||
3446 | 238 | mtime)) | ||
3447 | 239 | return False | ||
3448 | 240 | |||
3449 | 241 | def config_updated_since(self, sentry_unit, filename, mtime, | ||
3450 | 242 | sleep_time=20): | ||
3451 | 243 | """Check if file was modified after a given time. | ||
3452 | 244 | |||
3453 | 245 | Args: | ||
3454 | 246 | sentry_unit (sentry): The sentry unit to check the file mtime on | ||
3455 | 247 | filename (string): The file to check mtime of | ||
3456 | 248 | mtime (float): The epoch time to check against | ||
3457 | 249 | sleep_time (int): Seconds to sleep before looking for process | ||
3458 | 250 | |||
3459 | 251 | Returns: | ||
3460 | 252 | bool: True if file was modified more recently than mtime, False if | ||
3461 | 253 | file was modified before mtime, | ||
3462 | 254 | """ | ||
3463 | 255 | self.log.debug('Checking %s updated since %s' % (filename, mtime)) | ||
3464 | 256 | time.sleep(sleep_time) | ||
3465 | 257 | file_mtime = self._get_file_mtime(sentry_unit, filename) | ||
3466 | 258 | if file_mtime >= mtime: | ||
3467 | 259 | self.log.debug('File mtime is newer than provided mtime ' | ||
3468 | 260 | '(%s >= %s)' % (file_mtime, mtime)) | ||
3469 | 261 | return True | ||
3470 | 262 | else: | ||
3471 | 263 | self.log.warn('File mtime %s is older than provided mtime %s' | ||
3472 | 264 | % (file_mtime, mtime)) | ||
3473 | 265 | return False | ||
3474 | 266 | |||
3475 | 267 | def validate_service_config_changed(self, sentry_unit, mtime, service, | ||
3476 | 268 | filename, pgrep_full=False, | ||
3477 | 269 | sleep_time=20, retry_count=2): | ||
3478 | 270 | """Check service and file were updated after mtime | ||
3479 | 271 | |||
3480 | 272 | Args: | ||
3481 | 273 | sentry_unit (sentry): The sentry unit to check for the service on | ||
3482 | 274 | mtime (float): The epoch time to check against | ||
3483 | 275 | service (string): service name to look for in process table | ||
3484 | 276 | filename (string): The file to check mtime of | ||
3485 | 277 | pgrep_full (boolean): Use full command line search mode with pgrep | ||
3486 | 278 | sleep_time (int): Seconds to sleep before looking for process | ||
3487 | 279 | retry_count (int): If service is not found, how many times to retry | ||
3488 | 280 | |||
3489 | 281 | Typical Usage: | ||
3490 | 282 | u = OpenStackAmuletUtils(ERROR) | ||
3491 | 283 | ... | ||
3492 | 284 | mtime = u.get_sentry_time(self.cinder_sentry) | ||
3493 | 285 | self.d.configure('cinder', {'verbose': 'True', 'debug': 'True'}) | ||
3494 | 286 | if not u.validate_service_config_changed(self.cinder_sentry, | ||
3495 | 287 | mtime, | ||
3496 | 288 | 'cinder-api', | ||
3497 | 289 | '/etc/cinder/cinder.conf') | ||
3498 | 290 | amulet.raise_status(amulet.FAIL, msg='update failed') | ||
3499 | 291 | Returns: | ||
3500 | 292 | bool: True if both service and file where updated/restarted after | ||
3501 | 293 | mtime, False if service is older than mtime or if service was | ||
3502 | 294 | not found or if filename was modified before mtime. | ||
3503 | 295 | """ | ||
3504 | 296 | self.log.debug('Checking %s restarted since %s' % (service, mtime)) | ||
3505 | 297 | time.sleep(sleep_time) | ||
3506 | 298 | service_restart = self.service_restarted_since(sentry_unit, mtime, | ||
3507 | 299 | service, | ||
3508 | 300 | pgrep_full=pgrep_full, | ||
3509 | 301 | sleep_time=0, | ||
3510 | 302 | retry_count=retry_count) | ||
3511 | 303 | config_update = self.config_updated_since(sentry_unit, filename, mtime, | ||
3512 | 304 | sleep_time=0) | ||
3513 | 305 | return service_restart and config_update | ||
3514 | 306 | |||
3515 | 307 | def get_sentry_time(self, sentry_unit): | ||
3516 | 308 | """Return current epoch time on a sentry""" | ||
3517 | 309 | cmd = "date +'%s'" | ||
3518 | 310 | return float(sentry_unit.run(cmd)[0]) | ||
3519 | 311 | |||
3520 | 190 | def relation_error(self, name, data): | 312 | def relation_error(self, name, data): |
3521 | 191 | return 'unexpected relation data in {} - {}'.format(name, data) | 313 | return 'unexpected relation data in {} - {}'.format(name, data) |
3522 | 192 | 314 | ||
3523 | 193 | 315 | ||
3524 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
3525 | --- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:45:23 +0000 | |||
3526 | +++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-04-16 21:50:07 +0000 | |||
3527 | @@ -15,6 +15,7 @@ | |||
3528 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
3529 | 16 | 16 | ||
3530 | 17 | import six | 17 | import six |
3531 | 18 | from collections import OrderedDict | ||
3532 | 18 | from charmhelpers.contrib.amulet.deployment import ( | 19 | from charmhelpers.contrib.amulet.deployment import ( |
3533 | 19 | AmuletDeployment | 20 | AmuletDeployment |
3534 | 20 | ) | 21 | ) |
3535 | @@ -43,7 +44,7 @@ | |||
3536 | 43 | Determine if the local branch being tested is derived from its | 44 | Determine if the local branch being tested is derived from its |
3537 | 44 | stable or next (dev) branch, and based on this, use the corresonding | 45 | stable or next (dev) branch, and based on this, use the corresonding |
3538 | 45 | stable or next branches for the other_services.""" | 46 | stable or next branches for the other_services.""" |
3540 | 46 | base_charms = ['mysql', 'mongodb', 'rabbitmq-server'] | 47 | base_charms = ['mysql', 'mongodb'] |
3541 | 47 | 48 | ||
3542 | 48 | if self.stable: | 49 | if self.stable: |
3543 | 49 | for svc in other_services: | 50 | for svc in other_services: |
3544 | @@ -71,16 +72,19 @@ | |||
3545 | 71 | services.append(this_service) | 72 | services.append(this_service) |
3546 | 72 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', | 73 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
3547 | 73 | 'ceph-osd', 'ceph-radosgw'] | 74 | 'ceph-osd', 'ceph-radosgw'] |
3548 | 75 | # Openstack subordinate charms do not expose an origin option as that | ||
3549 | 76 | # is controlled by the principle | ||
3550 | 77 | ignore = ['neutron-openvswitch'] | ||
3551 | 74 | 78 | ||
3552 | 75 | if self.openstack: | 79 | if self.openstack: |
3553 | 76 | for svc in services: | 80 | for svc in services: |
3555 | 77 | if svc['name'] not in use_source: | 81 | if svc['name'] not in use_source + ignore: |
3556 | 78 | config = {'openstack-origin': self.openstack} | 82 | config = {'openstack-origin': self.openstack} |
3557 | 79 | self.d.configure(svc['name'], config) | 83 | self.d.configure(svc['name'], config) |
3558 | 80 | 84 | ||
3559 | 81 | if self.source: | 85 | if self.source: |
3560 | 82 | for svc in services: | 86 | for svc in services: |
3562 | 83 | if svc['name'] in use_source: | 87 | if svc['name'] in use_source and svc['name'] not in ignore: |
3563 | 84 | config = {'source': self.source} | 88 | config = {'source': self.source} |
3564 | 85 | self.d.configure(svc['name'], config) | 89 | self.d.configure(svc['name'], config) |
3565 | 86 | 90 | ||
3566 | @@ -97,12 +101,37 @@ | |||
3567 | 97 | """ | 101 | """ |
3568 | 98 | (self.precise_essex, self.precise_folsom, self.precise_grizzly, | 102 | (self.precise_essex, self.precise_folsom, self.precise_grizzly, |
3569 | 99 | self.precise_havana, self.precise_icehouse, | 103 | self.precise_havana, self.precise_icehouse, |
3571 | 100 | self.trusty_icehouse) = range(6) | 104 | self.trusty_icehouse, self.trusty_juno, self.trusty_kilo, |
3572 | 105 | self.utopic_juno, self.vivid_kilo) = range(10) | ||
3573 | 101 | releases = { | 106 | releases = { |
3574 | 102 | ('precise', None): self.precise_essex, | 107 | ('precise', None): self.precise_essex, |
3575 | 103 | ('precise', 'cloud:precise-folsom'): self.precise_folsom, | 108 | ('precise', 'cloud:precise-folsom'): self.precise_folsom, |
3576 | 104 | ('precise', 'cloud:precise-grizzly'): self.precise_grizzly, | 109 | ('precise', 'cloud:precise-grizzly'): self.precise_grizzly, |
3577 | 105 | ('precise', 'cloud:precise-havana'): self.precise_havana, | 110 | ('precise', 'cloud:precise-havana'): self.precise_havana, |
3578 | 106 | ('precise', 'cloud:precise-icehouse'): self.precise_icehouse, | 111 | ('precise', 'cloud:precise-icehouse'): self.precise_icehouse, |
3580 | 107 | ('trusty', None): self.trusty_icehouse} | 112 | ('trusty', None): self.trusty_icehouse, |
3581 | 113 | ('trusty', 'cloud:trusty-juno'): self.trusty_juno, | ||
3582 | 114 | ('trusty', 'cloud:trusty-kilo'): self.trusty_kilo, | ||
3583 | 115 | ('utopic', None): self.utopic_juno, | ||
3584 | 116 | ('vivid', None): self.vivid_kilo} | ||
3585 | 108 | return releases[(self.series, self.openstack)] | 117 | return releases[(self.series, self.openstack)] |
3586 | 118 | |||
3587 | 119 | def _get_openstack_release_string(self): | ||
3588 | 120 | """Get openstack release string. | ||
3589 | 121 | |||
3590 | 122 | Return a string representing the openstack release. | ||
3591 | 123 | """ | ||
3592 | 124 | releases = OrderedDict([ | ||
3593 | 125 | ('precise', 'essex'), | ||
3594 | 126 | ('quantal', 'folsom'), | ||
3595 | 127 | ('raring', 'grizzly'), | ||
3596 | 128 | ('saucy', 'havana'), | ||
3597 | 129 | ('trusty', 'icehouse'), | ||
3598 | 130 | ('utopic', 'juno'), | ||
3599 | 131 | ('vivid', 'kilo'), | ||
3600 | 132 | ]) | ||
3601 | 133 | if self.openstack: | ||
3602 | 134 | os_origin = self.openstack.split(':')[1] | ||
3603 | 135 | return os_origin.split('%s-' % self.series)[1].split('/')[0] | ||
3604 | 136 | else: | ||
3605 | 137 | return releases[self.series] | ||
3606 | 109 | 138 | ||
3607 | === added directory 'trusty' | |||
3608 | === modified file 'unit_tests/__init__.py' | |||
3609 | --- unit_tests/__init__.py 2013-08-09 18:45:02 +0000 | |||
3610 | +++ unit_tests/__init__.py 2015-04-16 21:50:07 +0000 | |||
3611 | @@ -1,3 +1,4 @@ | |||
3612 | 1 | import sys | 1 | import sys |
3613 | 2 | 2 | ||
3614 | 3 | sys.path.append('actions/') | ||
3615 | 3 | sys.path.append('hooks/') | 4 | sys.path.append('hooks/') |
3616 | 4 | 5 | ||
3617 | === added file 'unit_tests/test_actions_git_reinstall.py' | |||
3618 | --- unit_tests/test_actions_git_reinstall.py 1970-01-01 00:00:00 +0000 | |||
3619 | +++ unit_tests/test_actions_git_reinstall.py 2015-04-16 21:50:07 +0000 | |||
3620 | @@ -0,0 +1,96 @@ | |||
3621 | 1 | from mock import patch | ||
3622 | 2 | import os | ||
3623 | 3 | |||
3624 | 4 | os.environ['JUJU_UNIT_NAME'] = 'glance' | ||
3625 | 5 | |||
3626 | 6 | with patch('glance_utils.register_configs') as register_configs: | ||
3627 | 7 | import git_reinstall | ||
3628 | 8 | |||
3629 | 9 | from test_utils import ( | ||
3630 | 10 | CharmTestCase | ||
3631 | 11 | ) | ||
3632 | 12 | |||
3633 | 13 | TO_PATCH = [ | ||
3634 | 14 | 'config', | ||
3635 | 15 | ] | ||
3636 | 16 | |||
3637 | 17 | |||
3638 | 18 | openstack_origin_git = \ | ||
3639 | 19 | """repositories: | ||
3640 | 20 | - {name: requirements, | ||
3641 | 21 | repository: 'git://git.openstack.org/openstack/requirements', | ||
3642 | 22 | branch: stable/juno} | ||
3643 | 23 | - {name: glance, | ||
3644 | 24 | repository: 'git://git.openstack.org/openstack/glance', | ||
3645 | 25 | branch: stable/juno}""" | ||
3646 | 26 | |||
3647 | 27 | |||
3648 | 28 | class TestGlanceActions(CharmTestCase): | ||
3649 | 29 | |||
3650 | 30 | def setUp(self): | ||
3651 | 31 | super(TestGlanceActions, self).setUp(git_reinstall, TO_PATCH) | ||
3652 | 32 | self.config.side_effect = self.test_config.get | ||
3653 | 33 | |||
3654 | 34 | @patch.object(git_reinstall, 'action_set') | ||
3655 | 35 | @patch.object(git_reinstall, 'action_fail') | ||
3656 | 36 | @patch.object(git_reinstall, 'git_install') | ||
3657 | 37 | @patch.object(git_reinstall, 'config_changed') | ||
3658 | 38 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
3659 | 39 | def test_git_reinstall(self, _config, config_changed, git_install, | ||
3660 | 40 | action_fail, action_set): | ||
3661 | 41 | _config.return_value = openstack_origin_git | ||
3662 | 42 | self.test_config.set('openstack-origin-git', openstack_origin_git) | ||
3663 | 43 | |||
3664 | 44 | git_reinstall.git_reinstall() | ||
3665 | 45 | |||
3666 | 46 | git_install.assert_called_with(openstack_origin_git) | ||
3667 | 47 | self.assertTrue(git_install.called) | ||
3668 | 48 | self.assertTrue(config_changed.called) | ||
3669 | 49 | self.assertFalse(action_set.called) | ||
3670 | 50 | self.assertFalse(action_fail.called) | ||
3671 | 51 | |||
3672 | 52 | @patch.object(git_reinstall, 'action_set') | ||
3673 | 53 | @patch.object(git_reinstall, 'action_fail') | ||
3674 | 54 | @patch.object(git_reinstall, 'git_install') | ||
3675 | 55 | @patch.object(git_reinstall, 'config_changed') | ||
3676 | 56 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
3677 | 57 | def test_git_reinstall_not_configured(self, _config, config_changed, | ||
3678 | 58 | git_install, action_fail, | ||
3679 | 59 | action_set): | ||
3680 | 60 | _config.return_value = None | ||
3681 | 61 | |||
3682 | 62 | git_reinstall.git_reinstall() | ||
3683 | 63 | |||
3684 | 64 | msg = 'openstack-origin-git is not configured' | ||
3685 | 65 | action_fail.assert_called_with(msg) | ||
3686 | 66 | self.assertFalse(git_install.called) | ||
3687 | 67 | self.assertFalse(action_set.called) | ||
3688 | 68 | |||
3689 | 69 | @patch.object(git_reinstall, 'action_set') | ||
3690 | 70 | @patch.object(git_reinstall, 'action_fail') | ||
3691 | 71 | @patch.object(git_reinstall, 'git_install') | ||
3692 | 72 | @patch.object(git_reinstall, 'config_changed') | ||
3693 | 73 | @patch('traceback.format_exc') | ||
3694 | 74 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
3695 | 75 | def test_git_reinstall_exception(self, _config, format_exc, | ||
3696 | 76 | config_changed, git_install, action_fail, | ||
3697 | 77 | action_set): | ||
3698 | 78 | _config.return_value = openstack_origin_git | ||
3699 | 79 | e = OSError('something bad happened') | ||
3700 | 80 | git_install.side_effect = e | ||
3701 | 81 | traceback = ( | ||
3702 | 82 | "Traceback (most recent call last):\n" | ||
3703 | 83 | " File \"actions/git_reinstall.py\", line 37, in git_reinstall\n" | ||
3704 | 84 | " git_install(config(\'openstack-origin-git\'))\n" | ||
3705 | 85 | " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 964, in __call__\n" # noqa | ||
3706 | 86 | " return _mock_self._mock_call(*args, **kwargs)\n" | ||
3707 | 87 | " File \"/usr/lib/python2.7/dist-packages/mock.py\", line 1019, in _mock_call\n" # noqa | ||
3708 | 88 | " raise effect\n" | ||
3709 | 89 | "OSError: something bad happened\n") | ||
3710 | 90 | format_exc.return_value = traceback | ||
3711 | 91 | |||
3712 | 92 | git_reinstall.git_reinstall() | ||
3713 | 93 | |||
3714 | 94 | msg = 'git-reinstall resulted in an unexpected error' | ||
3715 | 95 | action_fail.assert_called_with(msg) | ||
3716 | 96 | action_set.assert_called_with({'traceback': traceback}) | ||
3717 | 0 | 97 | ||
3718 | === modified file 'unit_tests/test_glance_relations.py' | |||
3719 | --- unit_tests/test_glance_relations.py 2015-01-22 16:55:57 +0000 | |||
3720 | +++ unit_tests/test_glance_relations.py 2015-04-16 21:50:07 +0000 | |||
3721 | @@ -1,6 +1,7 @@ | |||
3722 | 1 | from mock import call, patch, MagicMock | 1 | from mock import call, patch, MagicMock |
3723 | 2 | import json | 2 | import json |
3724 | 3 | import os | 3 | import os |
3725 | 4 | import yaml | ||
3726 | 4 | 5 | ||
3727 | 5 | from test_utils import CharmTestCase | 6 | from test_utils import CharmTestCase |
3728 | 6 | 7 | ||
3729 | @@ -38,10 +39,11 @@ | |||
3730 | 38 | 'apt_install', | 39 | 'apt_install', |
3731 | 39 | 'apt_update', | 40 | 'apt_update', |
3732 | 40 | 'restart_on_change', | 41 | 'restart_on_change', |
3733 | 42 | 'service_reload', | ||
3734 | 41 | 'service_stop', | 43 | 'service_stop', |
3735 | 42 | # charmhelpers.contrib.openstack.utils | 44 | # charmhelpers.contrib.openstack.utils |
3736 | 43 | 'configure_installation_source', | 45 | 'configure_installation_source', |
3738 | 44 | 'get_os_codename_package', | 46 | 'os_release', |
3739 | 45 | 'openstack_upgrade_available', | 47 | 'openstack_upgrade_available', |
3740 | 46 | # charmhelpers.contrib.hahelpers.cluster_utils | 48 | # charmhelpers.contrib.hahelpers.cluster_utils |
3741 | 47 | 'eligible_leader', | 49 | 'eligible_leader', |
3742 | @@ -52,6 +54,7 @@ | |||
3743 | 52 | 'migrate_database', | 54 | 'migrate_database', |
3744 | 53 | 'ensure_ceph_keyring', | 55 | 'ensure_ceph_keyring', |
3745 | 54 | 'ceph_config_file', | 56 | 'ceph_config_file', |
3746 | 57 | 'git_install', | ||
3747 | 55 | 'update_nrpe_config', | 58 | 'update_nrpe_config', |
3748 | 56 | # other | 59 | # other |
3749 | 57 | 'call', | 60 | 'call', |
3750 | @@ -64,6 +67,7 @@ | |||
3751 | 64 | 'get_iface_for_address', | 67 | 'get_iface_for_address', |
3752 | 65 | 'get_ipv6_addr', | 68 | 'get_ipv6_addr', |
3753 | 66 | 'sync_db_with_multi_ipv6_addresses', | 69 | 'sync_db_with_multi_ipv6_addresses', |
3754 | 70 | 'delete_keyring', | ||
3755 | 67 | ] | 71 | ] |
3756 | 68 | 72 | ||
3757 | 69 | 73 | ||
3758 | @@ -73,23 +77,26 @@ | |||
3759 | 73 | super(GlanceRelationTests, self).setUp(relations, TO_PATCH) | 77 | super(GlanceRelationTests, self).setUp(relations, TO_PATCH) |
3760 | 74 | self.config.side_effect = self.test_config.get | 78 | self.config.side_effect = self.test_config.get |
3761 | 75 | 79 | ||
3763 | 76 | def test_install_hook(self): | 80 | @patch.object(utils, 'git_install_requested') |
3764 | 81 | def test_install_hook(self, git_requested): | ||
3765 | 82 | git_requested.return_value = False | ||
3766 | 77 | repo = 'cloud:precise-grizzly' | 83 | repo = 'cloud:precise-grizzly' |
3767 | 78 | self.test_config.set('openstack-origin', repo) | 84 | self.test_config.set('openstack-origin', repo) |
3768 | 79 | self.service_stop.return_value = True | 85 | self.service_stop.return_value = True |
3769 | 80 | relations.install_hook() | 86 | relations.install_hook() |
3770 | 81 | self.configure_installation_source.assert_called_with(repo) | 87 | self.configure_installation_source.assert_called_with(repo) |
3771 | 82 | self.apt_update.assert_called_with(fatal=True) | 88 | self.apt_update.assert_called_with(fatal=True) |
3776 | 83 | self.apt_install.assert_called_with(['apache2', 'glance', | 89 | self.apt_install.assert_called_with(['haproxy', 'python-six', 'uuid', |
3777 | 84 | 'python-mysqldb', | 90 | 'python-mysqldb', 'apache2', |
3778 | 85 | 'python-swiftclient', | 91 | 'python-psycopg2', 'glance', |
3775 | 86 | 'python-psycopg2', | ||
3779 | 87 | 'python-keystone', | 92 | 'python-keystone', |
3782 | 88 | 'python-six', | 93 | 'python-swiftclient'], fatal=True) |
3781 | 89 | 'uuid', 'haproxy'], fatal=True) | ||
3783 | 90 | self.assertTrue(self.execd_preinstall.called) | 94 | self.assertTrue(self.execd_preinstall.called) |
3784 | 95 | self.git_install.assert_called_with(None) | ||
3785 | 91 | 96 | ||
3787 | 92 | def test_install_hook_precise_distro(self): | 97 | @patch.object(utils, 'git_install_requested') |
3788 | 98 | def test_install_hook_precise_distro(self, git_requested): | ||
3789 | 99 | git_requested.return_value = False | ||
3790 | 93 | self.test_config.set('openstack-origin', 'distro') | 100 | self.test_config.set('openstack-origin', 'distro') |
3791 | 94 | self.lsb_release.return_value = {'DISTRIB_RELEASE': 12.04, | 101 | self.lsb_release.return_value = {'DISTRIB_RELEASE': 12.04, |
3792 | 95 | 'DISTRIB_CODENAME': 'precise'} | 102 | 'DISTRIB_CODENAME': 'precise'} |
3793 | @@ -99,6 +106,37 @@ | |||
3794 | 99 | "cloud:precise-folsom" | 106 | "cloud:precise-folsom" |
3795 | 100 | ) | 107 | ) |
3796 | 101 | 108 | ||
3797 | 109 | @patch.object(utils, 'git_install_requested') | ||
3798 | 110 | def test_install_hook_git(self, git_requested): | ||
3799 | 111 | git_requested.return_value = True | ||
3800 | 112 | repo = 'cloud:trusty-juno' | ||
3801 | 113 | openstack_origin_git = { | ||
3802 | 114 | 'repositories': [ | ||
3803 | 115 | {'name': 'requirements', | ||
3804 | 116 | 'repository': 'git://git.openstack.org/openstack/requirements', # noqa | ||
3805 | 117 | 'branch': 'stable/juno'}, | ||
3806 | 118 | {'name': 'glance', | ||
3807 | 119 | 'repository': 'git://git.openstack.org/openstack/glance', | ||
3808 | 120 | 'branch': 'stable/juno'} | ||
3809 | 121 | ], | ||
3810 | 122 | 'directory': '/mnt/openstack-git', | ||
3811 | 123 | } | ||
3812 | 124 | projects_yaml = yaml.dump(openstack_origin_git) | ||
3813 | 125 | self.test_config.set('openstack-origin', repo) | ||
3814 | 126 | self.test_config.set('openstack-origin-git', projects_yaml) | ||
3815 | 127 | relations.install_hook() | ||
3816 | 128 | self.assertTrue(self.execd_preinstall.called) | ||
3817 | 129 | self.configure_installation_source.assert_called_with(repo) | ||
3818 | 130 | self.apt_update.assert_called_with(fatal=True) | ||
3819 | 131 | self.apt_install.assert_called_with(['haproxy', 'python-setuptools', | ||
3820 | 132 | 'python-six', 'uuid', | ||
3821 | 133 | 'python-mysqldb', 'python-pip', | ||
3822 | 134 | 'apache2', 'libxslt1-dev', | ||
3823 | 135 | 'python-psycopg2', 'zlib1g-dev', | ||
3824 | 136 | 'python-dev', 'libxml2-dev'], | ||
3825 | 137 | fatal=True) | ||
3826 | 138 | self.git_install.assert_called_with(projects_yaml) | ||
3827 | 139 | |||
3828 | 102 | def test_db_joined(self): | 140 | def test_db_joined(self): |
3829 | 103 | self.unit_get.return_value = 'glance.foohost.com' | 141 | self.unit_get.return_value = 'glance.foohost.com' |
3830 | 104 | self.is_relation_made.return_value = False | 142 | self.is_relation_made.return_value = False |
3831 | @@ -215,7 +253,7 @@ | |||
3832 | 215 | 253 | ||
3833 | 216 | @patch.object(relations, 'CONFIGS') | 254 | @patch.object(relations, 'CONFIGS') |
3834 | 217 | def test_db_changed_with_essex_not_setting_version_control(self, configs): | 255 | def test_db_changed_with_essex_not_setting_version_control(self, configs): |
3836 | 218 | self.get_os_codename_package.return_value = "essex" | 256 | self.os_release.return_value = "essex" |
3837 | 219 | self.call.return_value = 0 | 257 | self.call.return_value = 0 |
3838 | 220 | self._shared_db_test(configs, 'glance/0') | 258 | self._shared_db_test(configs, 'glance/0') |
3839 | 221 | self.assertEquals([call('/etc/glance/glance-registry.conf')], | 259 | self.assertEquals([call('/etc/glance/glance-registry.conf')], |
3840 | @@ -228,7 +266,7 @@ | |||
3841 | 228 | @patch.object(relations, 'CONFIGS') | 266 | @patch.object(relations, 'CONFIGS') |
3842 | 229 | def test_postgresql_db_changed_with_essex_not_setting_version_control( | 267 | def test_postgresql_db_changed_with_essex_not_setting_version_control( |
3843 | 230 | self, configs): | 268 | self, configs): |
3845 | 231 | self.get_os_codename_package.return_value = "essex" | 269 | self.os_release.return_value = "essex" |
3846 | 232 | self.call.return_value = 0 | 270 | self.call.return_value = 0 |
3847 | 233 | self._postgresql_db_test(configs) | 271 | self._postgresql_db_test(configs) |
3848 | 234 | self.assertEquals([call('/etc/glance/glance-registry.conf')], | 272 | self.assertEquals([call('/etc/glance/glance-registry.conf')], |
3849 | @@ -240,7 +278,7 @@ | |||
3850 | 240 | 278 | ||
3851 | 241 | @patch.object(relations, 'CONFIGS') | 279 | @patch.object(relations, 'CONFIGS') |
3852 | 242 | def test_db_changed_with_essex_setting_version_control(self, configs): | 280 | def test_db_changed_with_essex_setting_version_control(self, configs): |
3854 | 243 | self.get_os_codename_package.return_value = "essex" | 281 | self.os_release.return_value = "essex" |
3855 | 244 | self.call.return_value = 1 | 282 | self.call.return_value = 1 |
3856 | 245 | self._shared_db_test(configs, 'glance/0') | 283 | self._shared_db_test(configs, 'glance/0') |
3857 | 246 | self.assertEquals([call('/etc/glance/glance-registry.conf')], | 284 | self.assertEquals([call('/etc/glance/glance-registry.conf')], |
3858 | @@ -256,7 +294,7 @@ | |||
3859 | 256 | @patch.object(relations, 'CONFIGS') | 294 | @patch.object(relations, 'CONFIGS') |
3860 | 257 | def test_postgresql_db_changed_with_essex_setting_version_control( | 295 | def test_postgresql_db_changed_with_essex_setting_version_control( |
3861 | 258 | self, configs): | 296 | self, configs): |
3863 | 259 | self.get_os_codename_package.return_value = "essex" | 297 | self.os_release.return_value = "essex" |
3864 | 260 | self.call.return_value = 1 | 298 | self.call.return_value = 1 |
3865 | 261 | self._postgresql_db_test(configs) | 299 | self._postgresql_db_test(configs) |
3866 | 262 | self.assertEquals([call('/etc/glance/glance-registry.conf')], | 300 | self.assertEquals([call('/etc/glance/glance-registry.conf')], |
3867 | @@ -381,6 +419,13 @@ | |||
3868 | 381 | call(self.ceph_config_file())], | 419 | call(self.ceph_config_file())], |
3869 | 382 | configs.write.call_args_list) | 420 | configs.write.call_args_list) |
3870 | 383 | 421 | ||
3871 | 422 | @patch.object(relations, 'CONFIGS') | ||
3872 | 423 | def test_ceph_broken(self, configs): | ||
3873 | 424 | self.service_name.return_value = 'glance' | ||
3874 | 425 | relations.ceph_broken() | ||
3875 | 426 | self.delete_keyring.assert_called_with(service='glance') | ||
3876 | 427 | self.assertTrue(configs.write_all.called) | ||
3877 | 428 | |||
3878 | 384 | def test_keystone_joined(self): | 429 | def test_keystone_joined(self): |
3879 | 385 | self.canonical_url.return_value = 'http://glancehost' | 430 | self.canonical_url.return_value = 'http://glancehost' |
3880 | 386 | relations.keystone_joined() | 431 | relations.keystone_joined() |
3881 | @@ -433,8 +478,12 @@ | |||
3882 | 433 | @patch.object(relations, 'configure_https') | 478 | @patch.object(relations, 'configure_https') |
3883 | 434 | @patch.object(relations, 'object_store_joined') | 479 | @patch.object(relations, 'object_store_joined') |
3884 | 435 | @patch.object(relations, 'CONFIGS') | 480 | @patch.object(relations, 'CONFIGS') |
3887 | 436 | def test_keystone_changed_with_object_store_relation( | 481 | @patch.object(utils, 'git_install_requested') |
3888 | 437 | self, configs, object_store_joined, configure_https): | 482 | def test_keystone_changed_with_object_store_relation(self, git_requested, |
3889 | 483 | configs, | ||
3890 | 484 | object_store_joined, | ||
3891 | 485 | configure_https): | ||
3892 | 486 | git_requested.return_value = False | ||
3893 | 438 | configs.complete_contexts = MagicMock() | 487 | configs.complete_contexts = MagicMock() |
3894 | 439 | configs.complete_contexts.return_value = ['identity-service'] | 488 | configs.complete_contexts.return_value = ['identity-service'] |
3895 | 440 | configs.write = MagicMock() | 489 | configs.write = MagicMock() |
3896 | @@ -449,14 +498,20 @@ | |||
3897 | 449 | self.assertTrue(configure_https.called) | 498 | self.assertTrue(configure_https.called) |
3898 | 450 | 499 | ||
3899 | 451 | @patch.object(relations, 'configure_https') | 500 | @patch.object(relations, 'configure_https') |
3901 | 452 | def test_config_changed_no_openstack_upgrade(self, configure_https): | 501 | @patch.object(relations, 'git_install_requested') |
3902 | 502 | def test_config_changed_no_openstack_upgrade(self, git_requested, | ||
3903 | 503 | configure_https): | ||
3904 | 504 | git_requested.return_value = False | ||
3905 | 453 | self.openstack_upgrade_available.return_value = False | 505 | self.openstack_upgrade_available.return_value = False |
3906 | 454 | relations.config_changed() | 506 | relations.config_changed() |
3907 | 455 | self.open_port.assert_called_with(9292) | 507 | self.open_port.assert_called_with(9292) |
3908 | 456 | self.assertTrue(configure_https.called) | 508 | self.assertTrue(configure_https.called) |
3909 | 457 | 509 | ||
3910 | 458 | @patch.object(relations, 'configure_https') | 510 | @patch.object(relations, 'configure_https') |
3912 | 459 | def test_config_changed_with_openstack_upgrade(self, configure_https): | 511 | @patch.object(relations, 'git_install_requested') |
3913 | 512 | def test_config_changed_with_openstack_upgrade(self, git_requested, | ||
3914 | 513 | configure_https): | ||
3915 | 514 | git_requested.return_value = False | ||
3916 | 460 | self.openstack_upgrade_available.return_value = True | 515 | self.openstack_upgrade_available.return_value = True |
3917 | 461 | relations.config_changed() | 516 | relations.config_changed() |
3918 | 462 | self.juju_log.assert_called_with( | 517 | self.juju_log.assert_called_with( |
3919 | @@ -465,6 +520,32 @@ | |||
3920 | 465 | self.assertTrue(self.do_openstack_upgrade.called) | 520 | self.assertTrue(self.do_openstack_upgrade.called) |
3921 | 466 | self.assertTrue(configure_https.called) | 521 | self.assertTrue(configure_https.called) |
3922 | 467 | 522 | ||
3923 | 523 | @patch.object(relations, 'configure_https') | ||
3924 | 524 | @patch.object(relations, 'git_install_requested') | ||
3925 | 525 | @patch.object(relations, 'config_value_changed') | ||
3926 | 526 | def test_config_changed_git_updated(self, config_val_changed, | ||
3927 | 527 | git_requested, configure_https): | ||
3928 | 528 | git_requested.return_value = True | ||
3929 | 529 | repo = 'cloud:trusty-juno' | ||
3930 | 530 | openstack_origin_git = { | ||
3931 | 531 | 'repositories': [ | ||
3932 | 532 | {'name': 'requirements', | ||
3933 | 533 | 'repository': 'git://git.openstack.org/openstack/requirements', # noqa | ||
3934 | 534 | 'branch': 'stable/juno'}, | ||
3935 | 535 | {'name': 'glance', | ||
3936 | 536 | 'repository': 'git://git.openstack.org/openstack/glance', | ||
3937 | 537 | 'branch': 'stable/juno'} | ||
3938 | 538 | ], | ||
3939 | 539 | 'directory': '/mnt/openstack-git', | ||
3940 | 540 | } | ||
3941 | 541 | projects_yaml = yaml.dump(openstack_origin_git) | ||
3942 | 542 | self.test_config.set('openstack-origin', repo) | ||
3943 | 543 | self.test_config.set('openstack-origin-git', projects_yaml) | ||
3944 | 544 | relations.config_changed() | ||
3945 | 545 | self.git_install.assert_called_with(projects_yaml) | ||
3946 | 546 | self.assertFalse(self.do_openstack_upgrade.called) | ||
3947 | 547 | self.assertTrue(configure_https.called) | ||
3948 | 548 | |||
3949 | 468 | @patch.object(relations, 'CONFIGS') | 549 | @patch.object(relations, 'CONFIGS') |
3950 | 469 | def test_cluster_changed(self, configs): | 550 | def test_cluster_changed(self, configs): |
3951 | 470 | self.test_config.set('prefer-ipv6', False) | 551 | self.test_config.set('prefer-ipv6', False) |
3952 | @@ -491,7 +572,9 @@ | |||
3953 | 491 | configs.write.call_args_list) | 572 | configs.write.call_args_list) |
3954 | 492 | 573 | ||
3955 | 493 | @patch.object(relations, 'CONFIGS') | 574 | @patch.object(relations, 'CONFIGS') |
3957 | 494 | def test_upgrade_charm(self, configs): | 575 | @patch.object(utils, 'git_install_requested') |
3958 | 576 | def test_upgrade_charm(self, git_requested, configs): | ||
3959 | 577 | git_requested.return_value = False | ||
3960 | 495 | self.filter_installed_packages.return_value = ['test'] | 578 | self.filter_installed_packages.return_value = ['test'] |
3961 | 496 | relations.upgrade_charm() | 579 | relations.upgrade_charm() |
3962 | 497 | self.apt_install.assert_called_with(['test'], fatal=True) | 580 | self.apt_install.assert_called_with(['test'], fatal=True) |
3963 | @@ -596,8 +679,9 @@ | |||
3964 | 596 | configs.write = MagicMock() | 679 | configs.write = MagicMock() |
3965 | 597 | self.relation_ids.return_value = ['identity-service:0'] | 680 | self.relation_ids.return_value = ['identity-service:0'] |
3966 | 598 | relations.configure_https() | 681 | relations.configure_https() |
3969 | 599 | cmd = ['a2ensite', 'openstack_https_frontend'] | 682 | calls = [call('a2dissite', 'openstack_https_frontend'), |
3970 | 600 | self.check_call.assert_called_with(cmd) | 683 | call('service', 'apache2', 'reload')] |
3971 | 684 | self.check_call.assert_called_has_calls(calls) | ||
3972 | 601 | keystone_joined.assert_called_with(relation_id='identity-service:0') | 685 | keystone_joined.assert_called_with(relation_id='identity-service:0') |
3973 | 602 | 686 | ||
3974 | 603 | @patch.object(relations, 'keystone_joined') | 687 | @patch.object(relations, 'keystone_joined') |
3975 | @@ -609,8 +693,9 @@ | |||
3976 | 609 | configs.write = MagicMock() | 693 | configs.write = MagicMock() |
3977 | 610 | self.relation_ids.return_value = ['identity-service:0'] | 694 | self.relation_ids.return_value = ['identity-service:0'] |
3978 | 611 | relations.configure_https() | 695 | relations.configure_https() |
3981 | 612 | cmd = ['a2dissite', 'openstack_https_frontend'] | 696 | calls = [call('a2dissite', 'openstack_https_frontend'), |
3982 | 613 | self.check_call.assert_called_with(cmd) | 697 | call('service', 'apache2', 'reload')] |
3983 | 698 | self.check_call.assert_called_has_calls(calls) | ||
3984 | 614 | keystone_joined.assert_called_with(relation_id='identity-service:0') | 699 | keystone_joined.assert_called_with(relation_id='identity-service:0') |
3985 | 615 | 700 | ||
3986 | 616 | @patch.object(relations, 'image_service_joined') | 701 | @patch.object(relations, 'image_service_joined') |
3987 | @@ -622,8 +707,9 @@ | |||
3988 | 622 | configs.write = MagicMock() | 707 | configs.write = MagicMock() |
3989 | 623 | self.relation_ids.return_value = ['image-service:0'] | 708 | self.relation_ids.return_value = ['image-service:0'] |
3990 | 624 | relations.configure_https() | 709 | relations.configure_https() |
3993 | 625 | cmd = ['a2ensite', 'openstack_https_frontend'] | 710 | calls = [call('a2dissite', 'openstack_https_frontend'), |
3994 | 626 | self.check_call.assert_called_with(cmd) | 711 | call('service', 'apache2', 'reload')] |
3995 | 712 | self.check_call.assert_called_has_calls(calls) | ||
3996 | 627 | image_service_joined.assert_called_with(relation_id='image-service:0') | 713 | image_service_joined.assert_called_with(relation_id='image-service:0') |
3997 | 628 | 714 | ||
3998 | 629 | @patch.object(relations, 'image_service_joined') | 715 | @patch.object(relations, 'image_service_joined') |
3999 | @@ -635,8 +721,9 @@ | |||
4000 | 635 | configs.write = MagicMock() | 721 | configs.write = MagicMock() |
4001 | 636 | self.relation_ids.return_value = ['image-service:0'] | 722 | self.relation_ids.return_value = ['image-service:0'] |
4002 | 637 | relations.configure_https() | 723 | relations.configure_https() |
4005 | 638 | cmd = ['a2dissite', 'openstack_https_frontend'] | 724 | calls = [call('a2dissite', 'openstack_https_frontend'), |
4006 | 639 | self.check_call.assert_called_with(cmd) | 725 | call('service', 'apache2', 'reload')] |
4007 | 726 | self.check_call.assert_called_has_calls(calls) | ||
4008 | 640 | image_service_joined.assert_called_with(relation_id='image-service:0') | 727 | image_service_joined.assert_called_with(relation_id='image-service:0') |
4009 | 641 | 728 | ||
4010 | 642 | def test_amqp_joined(self): | 729 | def test_amqp_joined(self): |
4011 | 643 | 730 | ||
4012 | === modified file 'unit_tests/test_glance_utils.py' | |||
4013 | --- unit_tests/test_glance_utils.py 2015-01-06 15:09:51 +0000 | |||
4014 | +++ unit_tests/test_glance_utils.py 2015-04-16 21:50:07 +0000 | |||
4015 | @@ -14,7 +14,6 @@ | |||
4016 | 14 | 'config', | 14 | 'config', |
4017 | 15 | 'log', | 15 | 'log', |
4018 | 16 | 'relation_ids', | 16 | 'relation_ids', |
4019 | 17 | 'get_os_codename_package', | ||
4020 | 18 | 'get_os_codename_install_source', | 17 | 'get_os_codename_install_source', |
4021 | 19 | 'configure_installation_source', | 18 | 'configure_installation_source', |
4022 | 20 | 'eligible_leader', | 19 | 'eligible_leader', |
4023 | @@ -23,6 +22,7 @@ | |||
4024 | 23 | 'apt_upgrade', | 22 | 'apt_upgrade', |
4025 | 24 | 'apt_install', | 23 | 'apt_install', |
4026 | 25 | 'mkdir', | 24 | 'mkdir', |
4027 | 25 | 'os_release', | ||
4028 | 26 | 'service_start', | 26 | 'service_start', |
4029 | 27 | 'service_stop', | 27 | 'service_stop', |
4030 | 28 | 'service_name', | 28 | 'service_name', |
4031 | @@ -34,6 +34,15 @@ | |||
4032 | 34 | '--option', 'Dpkg::Options::=--force-confdef', | 34 | '--option', 'Dpkg::Options::=--force-confdef', |
4033 | 35 | ] | 35 | ] |
4034 | 36 | 36 | ||
4035 | 37 | openstack_origin_git = \ | ||
4036 | 38 | """repositories: | ||
4037 | 39 | - {name: requirements, | ||
4038 | 40 | repository: 'git://git.openstack.org/openstack/requirements', | ||
4039 | 41 | branch: stable/juno} | ||
4040 | 42 | - {name: glance, | ||
4041 | 43 | repository: 'git://git.openstack.org/openstack/glance', | ||
4042 | 44 | branch: stable/juno}""" | ||
4043 | 45 | |||
4044 | 37 | 46 | ||
4045 | 38 | class TestGlanceUtils(CharmTestCase): | 47 | class TestGlanceUtils(CharmTestCase): |
4046 | 39 | 48 | ||
4047 | @@ -50,7 +59,7 @@ | |||
4048 | 50 | @patch('os.path.exists') | 59 | @patch('os.path.exists') |
4049 | 51 | def test_register_configs_apache(self, exists): | 60 | def test_register_configs_apache(self, exists): |
4050 | 52 | exists.return_value = False | 61 | exists.return_value = False |
4052 | 53 | self.get_os_codename_package.return_value = 'grizzly' | 62 | self.os_release.return_value = 'grizzly' |
4053 | 54 | self.relation_ids.return_value = False | 63 | self.relation_ids.return_value = False |
4054 | 55 | configs = utils.register_configs() | 64 | configs = utils.register_configs() |
4055 | 56 | calls = [] | 65 | calls = [] |
4056 | @@ -69,7 +78,7 @@ | |||
4057 | 69 | @patch('os.path.exists') | 78 | @patch('os.path.exists') |
4058 | 70 | def test_register_configs_apache24(self, exists): | 79 | def test_register_configs_apache24(self, exists): |
4059 | 71 | exists.return_value = True | 80 | exists.return_value = True |
4061 | 72 | self.get_os_codename_package.return_value = 'grizzly' | 81 | self.os_release.return_value = 'grizzly' |
4062 | 73 | self.relation_ids.return_value = False | 82 | self.relation_ids.return_value = False |
4063 | 74 | configs = utils.register_configs() | 83 | configs = utils.register_configs() |
4064 | 75 | calls = [] | 84 | calls = [] |
4065 | @@ -88,7 +97,7 @@ | |||
4066 | 88 | @patch('os.path.exists') | 97 | @patch('os.path.exists') |
4067 | 89 | def test_register_configs_ceph(self, exists): | 98 | def test_register_configs_ceph(self, exists): |
4068 | 90 | exists.return_value = True | 99 | exists.return_value = True |
4070 | 91 | self.get_os_codename_package.return_value = 'grizzly' | 100 | self.os_release.return_value = 'grizzly' |
4071 | 92 | self.relation_ids.return_value = ['ceph:0'] | 101 | self.relation_ids.return_value = ['ceph:0'] |
4072 | 93 | self.service_name.return_value = 'glance' | 102 | self.service_name.return_value = 'glance' |
4073 | 94 | configs = utils.register_configs() | 103 | configs = utils.register_configs() |
4074 | @@ -121,8 +130,26 @@ | |||
4075 | 121 | ]) | 130 | ]) |
4076 | 122 | self.assertEquals(ex_map, utils.restart_map()) | 131 | self.assertEquals(ex_map, utils.restart_map()) |
4077 | 123 | 132 | ||
4078 | 133 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
4079 | 134 | def test_determine_packages(self, _config): | ||
4080 | 135 | _config.return_value = None | ||
4081 | 136 | result = utils.determine_packages() | ||
4082 | 137 | ex = utils.PACKAGES | ||
4083 | 138 | self.assertEquals(set(ex), set(result)) | ||
4084 | 139 | |||
4085 | 140 | @patch('charmhelpers.contrib.openstack.utils.config') | ||
4086 | 141 | def test_determine_packages_git(self, _config): | ||
4087 | 142 | _config.return_value = openstack_origin_git | ||
4088 | 143 | result = utils.determine_packages() | ||
4089 | 144 | ex = utils.PACKAGES + utils.BASE_GIT_PACKAGES | ||
4090 | 145 | for p in utils.GIT_PACKAGE_BLACKLIST: | ||
4091 | 146 | ex.remove(p) | ||
4092 | 147 | self.assertEquals(set(ex), set(result)) | ||
4093 | 148 | |||
4094 | 124 | @patch.object(utils, 'migrate_database') | 149 | @patch.object(utils, 'migrate_database') |
4096 | 125 | def test_openstack_upgrade_leader(self, migrate): | 150 | @patch.object(utils, 'git_install_requested') |
4097 | 151 | def test_openstack_upgrade_leader(self, git_requested, migrate): | ||
4098 | 152 | git_requested.return_value = True | ||
4099 | 126 | self.config.side_effect = None | 153 | self.config.side_effect = None |
4100 | 127 | self.config.return_value = 'cloud:precise-havana' | 154 | self.config.return_value = 'cloud:precise-havana' |
4101 | 128 | self.eligible_leader.return_value = True | 155 | self.eligible_leader.return_value = True |
4102 | @@ -130,14 +157,17 @@ | |||
4103 | 130 | configs = MagicMock() | 157 | configs = MagicMock() |
4104 | 131 | utils.do_openstack_upgrade(configs) | 158 | utils.do_openstack_upgrade(configs) |
4105 | 132 | self.assertTrue(configs.write_all.called) | 159 | self.assertTrue(configs.write_all.called) |
4107 | 133 | self.apt_install.assert_called_with(utils.PACKAGES, fatal=True) | 160 | self.apt_install.assert_called_with(utils.determine_packages(), |
4108 | 161 | fatal=True) | ||
4109 | 134 | self.apt_upgrade.assert_called_with(options=DPKG_OPTS, | 162 | self.apt_upgrade.assert_called_with(options=DPKG_OPTS, |
4110 | 135 | fatal=True, dist=True) | 163 | fatal=True, dist=True) |
4111 | 136 | configs.set_release.assert_called_with(openstack_release='havana') | 164 | configs.set_release.assert_called_with(openstack_release='havana') |
4112 | 137 | self.assertTrue(migrate.called) | 165 | self.assertTrue(migrate.called) |
4113 | 138 | 166 | ||
4114 | 139 | @patch.object(utils, 'migrate_database') | 167 | @patch.object(utils, 'migrate_database') |
4116 | 140 | def test_openstack_upgrade_not_leader(self, migrate): | 168 | @patch.object(utils, 'git_install_requested') |
4117 | 169 | def test_openstack_upgrade_not_leader(self, git_requested, migrate): | ||
4118 | 170 | git_requested.return_value = True | ||
4119 | 141 | self.config.side_effect = None | 171 | self.config.side_effect = None |
4120 | 142 | self.config.return_value = 'cloud:precise-havana' | 172 | self.config.return_value = 'cloud:precise-havana' |
4121 | 143 | self.eligible_leader.return_value = False | 173 | self.eligible_leader.return_value = False |
4122 | @@ -145,8 +175,111 @@ | |||
4123 | 145 | configs = MagicMock() | 175 | configs = MagicMock() |
4124 | 146 | utils.do_openstack_upgrade(configs) | 176 | utils.do_openstack_upgrade(configs) |
4125 | 147 | self.assertTrue(configs.write_all.called) | 177 | self.assertTrue(configs.write_all.called) |
4127 | 148 | self.apt_install.assert_called_with(utils.PACKAGES, fatal=True) | 178 | self.apt_install.assert_called_with(utils.determine_packages(), |
4128 | 179 | fatal=True) | ||
4129 | 149 | self.apt_upgrade.assert_called_with(options=DPKG_OPTS, | 180 | self.apt_upgrade.assert_called_with(options=DPKG_OPTS, |
4130 | 150 | fatal=True, dist=True) | 181 | fatal=True, dist=True) |
4131 | 151 | configs.set_release.assert_called_with(openstack_release='havana') | 182 | configs.set_release.assert_called_with(openstack_release='havana') |
4132 | 152 | self.assertFalse(migrate.called) | 183 | self.assertFalse(migrate.called) |
4133 | 184 | |||
4134 | 185 | @patch.object(utils, 'git_install_requested') | ||
4135 | 186 | @patch.object(utils, 'git_clone_and_install') | ||
4136 | 187 | @patch.object(utils, 'git_post_install') | ||
4137 | 188 | @patch.object(utils, 'git_pre_install') | ||
4138 | 189 | def test_git_install(self, git_pre, git_post, git_clone_and_install, | ||
4139 | 190 | git_requested): | ||
4140 | 191 | projects_yaml = openstack_origin_git | ||
4141 | 192 | git_requested.return_value = True | ||
4142 | 193 | utils.git_install(projects_yaml) | ||
4143 | 194 | self.assertTrue(git_pre.called) | ||
4144 | 195 | git_clone_and_install.assert_called_with(openstack_origin_git, | ||
4145 | 196 | core_project='glance') | ||
4146 | 197 | self.assertTrue(git_post.called) | ||
4147 | 198 | |||
4148 | 199 | @patch.object(utils, 'mkdir') | ||
4149 | 200 | @patch.object(utils, 'write_file') | ||
4150 | 201 | @patch.object(utils, 'add_user_to_group') | ||
4151 | 202 | @patch.object(utils, 'add_group') | ||
4152 | 203 | @patch.object(utils, 'adduser') | ||
4153 | 204 | def test_git_pre_install(self, adduser, add_group, add_user_to_group, | ||
4154 | 205 | write_file, mkdir): | ||
4155 | 206 | utils.git_pre_install() | ||
4156 | 207 | adduser.assert_called_with('glance', shell='/bin/bash', | ||
4157 | 208 | system_user=True) | ||
4158 | 209 | add_group.assert_called_with('glance', system_group=True) | ||
4159 | 210 | add_user_to_group.assert_called_with('glance', 'glance') | ||
4160 | 211 | expected = [ | ||
4161 | 212 | call('/var/lib/glance', owner='glance', | ||
4162 | 213 | group='glance', perms=0700, force=False), | ||
4163 | 214 | call('/var/lib/glance/images', owner='glance', | ||
4164 | 215 | group='glance', perms=0700, force=False), | ||
4165 | 216 | call('/var/lib/glance/image-cache', owner='glance', | ||
4166 | 217 | group='glance', perms=0700, force=False), | ||
4167 | 218 | call('/var/lib/glance/image-cache/incomplete', owner='glance', | ||
4168 | 219 | group='glance', perms=0700, force=False), | ||
4169 | 220 | call('/var/lib/glance/image-cache/invalid', owner='glance', | ||
4170 | 221 | group='glance', perms=0700, force=False), | ||
4171 | 222 | call('/var/lib/glance/image-cache/queue', owner='glance', | ||
4172 | 223 | group='glance', perms=0700, force=False), | ||
4173 | 224 | call('/var/log/glance', owner='glance', | ||
4174 | 225 | group='glance', perms=0700, force=False), | ||
4175 | 226 | ] | ||
4176 | 227 | self.assertEquals(mkdir.call_args_list, expected) | ||
4177 | 228 | expected = [ | ||
4178 | 229 | call('/var/log/glance/glance-api.log', '', owner='glance', | ||
4179 | 230 | group='glance', perms=0600), | ||
4180 | 231 | call('/var/log/glance/glance-registry.log', '', owner='glance', | ||
4181 | 232 | group='glance', perms=0600), | ||
4182 | 233 | ] | ||
4183 | 234 | self.assertEquals(write_file.call_args_list, expected) | ||
4184 | 235 | |||
4185 | 236 | @patch.object(utils, 'git_src_dir') | ||
4186 | 237 | @patch.object(utils, 'service_restart') | ||
4187 | 238 | @patch.object(utils, 'render') | ||
4188 | 239 | @patch('os.path.join') | ||
4189 | 240 | @patch('os.path.exists') | ||
4190 | 241 | @patch('shutil.copytree') | ||
4191 | 242 | @patch('shutil.rmtree') | ||
4192 | 243 | def test_git_post_install(self, rmtree, copytree, exists, join, render, | ||
4193 | 244 | service_restart, git_src_dir): | ||
4194 | 245 | projects_yaml = openstack_origin_git | ||
4195 | 246 | join.return_value = 'joined-string' | ||
4196 | 247 | utils.git_post_install(projects_yaml) | ||
4197 | 248 | expected = [ | ||
4198 | 249 | call('joined-string', '/etc/glance'), | ||
4199 | 250 | ] | ||
4200 | 251 | copytree.assert_has_calls(expected) | ||
4201 | 252 | glance_api_context = { | ||
4202 | 253 | 'service_description': 'Glance API server', | ||
4203 | 254 | 'service_name': 'Glance', | ||
4204 | 255 | 'user_name': 'glance', | ||
4205 | 256 | 'start_dir': '/var/lib/glance', | ||
4206 | 257 | 'process_name': 'glance-api', | ||
4207 | 258 | 'executable_name': '/usr/local/bin/glance-api', | ||
4208 | 259 | 'config_files': ['/etc/glance/glance-api.conf'], | ||
4209 | 260 | 'log_file': '/var/log/glance/api.log', | ||
4210 | 261 | } | ||
4211 | 262 | glance_registry_context = { | ||
4212 | 263 | 'service_description': 'Glance registry server', | ||
4213 | 264 | 'service_name': 'Glance', | ||
4214 | 265 | 'user_name': 'glance', | ||
4215 | 266 | 'start_dir': '/var/lib/glance', | ||
4216 | 267 | 'process_name': 'glance-registry', | ||
4217 | 268 | 'executable_name': '/usr/local/bin/glance-registry', | ||
4218 | 269 | 'config_files': ['/etc/glance/glance-registry.conf'], | ||
4219 | 270 | 'log_file': '/var/log/glance/registry.log', | ||
4220 | 271 | } | ||
4221 | 272 | expected = [ | ||
4222 | 273 | call('git.upstart', '/etc/init/glance-api.conf', | ||
4223 | 274 | glance_api_context, perms=0o644, | ||
4224 | 275 | templates_dir='joined-string'), | ||
4225 | 276 | call('git.upstart', '/etc/init/glance-registry.conf', | ||
4226 | 277 | glance_registry_context, perms=0o644, | ||
4227 | 278 | templates_dir='joined-string'), | ||
4228 | 279 | ] | ||
4229 | 280 | self.assertEquals(render.call_args_list, expected) | ||
4230 | 281 | expected = [ | ||
4231 | 282 | call('glance-api'), | ||
4232 | 283 | call('glance-registry'), | ||
4233 | 284 | ] | ||
4234 | 285 | self.assertEquals(service_restart.call_args_list, expected) |
charm_lint_check #3538 glance for 1chb1n mp256581
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/3538/