Merge lp:~corey.bryant/charms/trusty/quantum-gateway/amulet-updates into lp:~openstack-charmers/charms/trusty/quantum-gateway/next
- Trusty Tahr (14.04)
- amulet-updates
- Merge into next
Proposed by
Corey Bryant
Status: | Merged |
---|---|
Merged at revision: | 69 |
Proposed branch: | lp:~corey.bryant/charms/trusty/quantum-gateway/amulet-updates |
Merge into: | lp:~openstack-charmers/charms/trusty/quantum-gateway/next |
Diff against target: |
1141 lines (+490/-186) 14 files modified
Makefile (+2/-1) hooks/charmhelpers/contrib/hahelpers/apache.py (+10/-3) hooks/charmhelpers/contrib/hahelpers/cluster.py (+1/-2) hooks/charmhelpers/contrib/network/ip.py (+101/-14) hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+30/-33) hooks/charmhelpers/contrib/openstack/context.py (+201/-71) hooks/charmhelpers/contrib/openstack/ip.py (+1/-1) hooks/charmhelpers/contrib/openstack/utils.py (+28/-1) hooks/charmhelpers/core/sysctl.py (+34/-0) tests/00-setup (+5/-4) tests/README (+6/-0) tests/basic_deployment.py (+26/-13) tests/charmhelpers/contrib/amulet/deployment.py (+15/-10) tests/charmhelpers/contrib/openstack/amulet/deployment.py (+30/-33) |
To merge this branch: | bzr merge lp:~corey.bryant/charms/trusty/quantum-gateway/amulet-updates |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenStack Charmers | Pending | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2014-07-29 07:46:01 +0000 | |||
3 | +++ Makefile 2014-10-07 21:21:43 +0000 | |||
4 | @@ -23,7 +23,8 @@ | |||
5 | 23 | # coreycb note: The -v should only be temporary until Amulet sends | 23 | # coreycb note: The -v should only be temporary until Amulet sends |
6 | 24 | # raise_status() messages to stderr: | 24 | # raise_status() messages to stderr: |
7 | 25 | # https://bugs.launchpad.net/amulet/+bug/1320357 | 25 | # https://bugs.launchpad.net/amulet/+bug/1320357 |
9 | 26 | @juju test -v -p AMULET_HTTP_PROXY | 26 | @juju test -v -p AMULET_HTTP_PROXY --timeout 900 \ |
10 | 27 | 00-setup 14-basic-precise-icehouse 15-basic-trusty-icehouse | ||
11 | 27 | 28 | ||
12 | 28 | publish: lint unit_test | 29 | publish: lint unit_test |
13 | 29 | bzr push lp:charms/quantum-gateway | 30 | bzr push lp:charms/quantum-gateway |
14 | 30 | 31 | ||
15 | === modified file 'hooks/charmhelpers/contrib/hahelpers/apache.py' | |||
16 | --- hooks/charmhelpers/contrib/hahelpers/apache.py 2014-03-27 11:20:28 +0000 | |||
17 | +++ hooks/charmhelpers/contrib/hahelpers/apache.py 2014-10-07 21:21:43 +0000 | |||
18 | @@ -20,20 +20,27 @@ | |||
19 | 20 | ) | 20 | ) |
20 | 21 | 21 | ||
21 | 22 | 22 | ||
23 | 23 | def get_cert(): | 23 | def get_cert(cn=None): |
24 | 24 | # TODO: deal with multiple https endpoints via charm config | ||
25 | 24 | cert = config_get('ssl_cert') | 25 | cert = config_get('ssl_cert') |
26 | 25 | key = config_get('ssl_key') | 26 | key = config_get('ssl_key') |
27 | 26 | if not (cert and key): | 27 | if not (cert and key): |
28 | 27 | log("Inspecting identity-service relations for SSL certificate.", | 28 | log("Inspecting identity-service relations for SSL certificate.", |
29 | 28 | level=INFO) | 29 | level=INFO) |
30 | 29 | cert = key = None | 30 | cert = key = None |
31 | 31 | if cn: | ||
32 | 32 | ssl_cert_attr = 'ssl_cert_{}'.format(cn) | ||
33 | 33 | ssl_key_attr = 'ssl_key_{}'.format(cn) | ||
34 | 34 | else: | ||
35 | 35 | ssl_cert_attr = 'ssl_cert' | ||
36 | 36 | ssl_key_attr = 'ssl_key' | ||
37 | 30 | for r_id in relation_ids('identity-service'): | 37 | for r_id in relation_ids('identity-service'): |
38 | 31 | for unit in relation_list(r_id): | 38 | for unit in relation_list(r_id): |
39 | 32 | if not cert: | 39 | if not cert: |
41 | 33 | cert = relation_get('ssl_cert', | 40 | cert = relation_get(ssl_cert_attr, |
42 | 34 | rid=r_id, unit=unit) | 41 | rid=r_id, unit=unit) |
43 | 35 | if not key: | 42 | if not key: |
45 | 36 | key = relation_get('ssl_key', | 43 | key = relation_get(ssl_key_attr, |
46 | 37 | rid=r_id, unit=unit) | 44 | rid=r_id, unit=unit) |
47 | 38 | return (cert, key) | 45 | return (cert, key) |
48 | 39 | 46 | ||
49 | 40 | 47 | ||
50 | === modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py' | |||
51 | --- hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-08-13 13:12:47 +0000 | |||
52 | +++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-10-07 21:21:43 +0000 | |||
53 | @@ -139,10 +139,9 @@ | |||
54 | 139 | return True | 139 | return True |
55 | 140 | for r_id in relation_ids('identity-service'): | 140 | for r_id in relation_ids('identity-service'): |
56 | 141 | for unit in relation_list(r_id): | 141 | for unit in relation_list(r_id): |
57 | 142 | # TODO - needs fixing for new helper as ssl_cert/key suffixes with CN | ||
58 | 142 | rel_state = [ | 143 | rel_state = [ |
59 | 143 | relation_get('https_keystone', rid=r_id, unit=unit), | 144 | relation_get('https_keystone', rid=r_id, unit=unit), |
60 | 144 | relation_get('ssl_cert', rid=r_id, unit=unit), | ||
61 | 145 | relation_get('ssl_key', rid=r_id, unit=unit), | ||
62 | 146 | relation_get('ca_cert', rid=r_id, unit=unit), | 145 | relation_get('ca_cert', rid=r_id, unit=unit), |
63 | 147 | ] | 146 | ] |
64 | 148 | # NOTE: works around (LP: #1203241) | 147 | # NOTE: works around (LP: #1203241) |
65 | 149 | 148 | ||
66 | === modified file 'hooks/charmhelpers/contrib/network/ip.py' | |||
67 | --- hooks/charmhelpers/contrib/network/ip.py 2014-09-19 10:56:29 +0000 | |||
68 | +++ hooks/charmhelpers/contrib/network/ip.py 2014-10-07 21:21:43 +0000 | |||
69 | @@ -1,11 +1,16 @@ | |||
70 | 1 | import glob | 1 | import glob |
71 | 2 | import re | ||
72 | 3 | import subprocess | ||
73 | 2 | import sys | 4 | import sys |
74 | 3 | 5 | ||
75 | 4 | from functools import partial | 6 | from functools import partial |
76 | 5 | 7 | ||
77 | 8 | from charmhelpers.core.hookenv import unit_get | ||
78 | 6 | from charmhelpers.fetch import apt_install | 9 | from charmhelpers.fetch import apt_install |
79 | 7 | from charmhelpers.core.hookenv import ( | 10 | from charmhelpers.core.hookenv import ( |
81 | 8 | ERROR, log, | 11 | WARNING, |
82 | 12 | ERROR, | ||
83 | 13 | log | ||
84 | 9 | ) | 14 | ) |
85 | 10 | 15 | ||
86 | 11 | try: | 16 | try: |
87 | @@ -52,6 +57,8 @@ | |||
88 | 52 | else: | 57 | else: |
89 | 53 | if fatal: | 58 | if fatal: |
90 | 54 | not_found_error_out() | 59 | not_found_error_out() |
91 | 60 | else: | ||
92 | 61 | return None | ||
93 | 55 | 62 | ||
94 | 56 | _validate_cidr(network) | 63 | _validate_cidr(network) |
95 | 57 | network = netaddr.IPNetwork(network) | 64 | network = netaddr.IPNetwork(network) |
96 | @@ -164,13 +171,14 @@ | |||
97 | 164 | if is_ipv6(address): | 171 | if is_ipv6(address): |
98 | 165 | address = "[%s]" % address | 172 | address = "[%s]" % address |
99 | 166 | else: | 173 | else: |
102 | 167 | log("Not an valid ipv6 address: %s" % address, | 174 | log("Not a valid ipv6 address: %s" % address, level=WARNING) |
101 | 168 | level=ERROR) | ||
103 | 169 | address = None | 175 | address = None |
104 | 176 | |||
105 | 170 | return address | 177 | return address |
106 | 171 | 178 | ||
107 | 172 | 179 | ||
109 | 173 | def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=True, exc_list=None): | 180 | def get_iface_addr(iface='eth0', inet_type='AF_INET', inc_aliases=False, |
110 | 181 | fatal=True, exc_list=None): | ||
111 | 174 | """ | 182 | """ |
112 | 175 | Return the assigned IP address for a given interface, if any, or []. | 183 | Return the assigned IP address for a given interface, if any, or []. |
113 | 176 | """ | 184 | """ |
114 | @@ -210,26 +218,105 @@ | |||
115 | 210 | if 'addr' in entry and entry['addr'] not in exc_list: | 218 | if 'addr' in entry and entry['addr'] not in exc_list: |
116 | 211 | addresses.append(entry['addr']) | 219 | addresses.append(entry['addr']) |
117 | 212 | if fatal and not addresses: | 220 | if fatal and not addresses: |
119 | 213 | raise Exception("Interface '%s' doesn't have any %s addresses." % (iface, inet_type)) | 221 | raise Exception("Interface '%s' doesn't have any %s addresses." % |
120 | 222 | (iface, inet_type)) | ||
121 | 214 | return addresses | 223 | return addresses |
122 | 215 | 224 | ||
123 | 216 | get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET') | 225 | get_ipv4_addr = partial(get_iface_addr, inet_type='AF_INET') |
124 | 217 | 226 | ||
125 | 218 | 227 | ||
127 | 219 | def get_ipv6_addr(iface='eth0', inc_aliases=False, fatal=True, exc_list=None): | 228 | def get_iface_from_addr(addr): |
128 | 229 | """Work out on which interface the provided address is configured.""" | ||
129 | 230 | for iface in netifaces.interfaces(): | ||
130 | 231 | addresses = netifaces.ifaddresses(iface) | ||
131 | 232 | for inet_type in addresses: | ||
132 | 233 | for _addr in addresses[inet_type]: | ||
133 | 234 | _addr = _addr['addr'] | ||
134 | 235 | # link local | ||
135 | 236 | ll_key = re.compile("(.+)%.*") | ||
136 | 237 | raw = re.match(ll_key, _addr) | ||
137 | 238 | if raw: | ||
138 | 239 | _addr = raw.group(1) | ||
139 | 240 | if _addr == addr: | ||
140 | 241 | log("Address '%s' is configured on iface '%s'" % | ||
141 | 242 | (addr, iface)) | ||
142 | 243 | return iface | ||
143 | 244 | |||
144 | 245 | msg = "Unable to infer net iface on which '%s' is configured" % (addr) | ||
145 | 246 | raise Exception(msg) | ||
146 | 247 | |||
147 | 248 | |||
148 | 249 | def sniff_iface(f): | ||
149 | 250 | """If no iface provided, inject net iface inferred from unit private | ||
150 | 251 | address. | ||
151 | 220 | """ | 252 | """ |
153 | 221 | Return the assigned IPv6 address for a given interface, if any, or []. | 253 | def iface_sniffer(*args, **kwargs): |
154 | 254 | if not kwargs.get('iface', None): | ||
155 | 255 | kwargs['iface'] = get_iface_from_addr(unit_get('private-address')) | ||
156 | 256 | |||
157 | 257 | return f(*args, **kwargs) | ||
158 | 258 | |||
159 | 259 | return iface_sniffer | ||
160 | 260 | |||
161 | 261 | |||
162 | 262 | @sniff_iface | ||
163 | 263 | def get_ipv6_addr(iface=None, inc_aliases=False, fatal=True, exc_list=None, | ||
164 | 264 | dynamic_only=True): | ||
165 | 265 | """Get assigned IPv6 address for a given interface. | ||
166 | 266 | |||
167 | 267 | Returns list of addresses found. If no address found, returns empty list. | ||
168 | 268 | |||
169 | 269 | If iface is None, we infer the current primary interface by doing a reverse | ||
170 | 270 | lookup on the unit private-address. | ||
171 | 271 | |||
172 | 272 | We currently only support scope global IPv6 addresses i.e. non-temporary | ||
173 | 273 | addresses. If no global IPv6 address is found, return the first one found | ||
174 | 274 | in the ipv6 address list. | ||
175 | 222 | """ | 275 | """ |
176 | 223 | addresses = get_iface_addr(iface=iface, inet_type='AF_INET6', | 276 | addresses = get_iface_addr(iface=iface, inet_type='AF_INET6', |
177 | 224 | inc_aliases=inc_aliases, fatal=fatal, | 277 | inc_aliases=inc_aliases, fatal=fatal, |
178 | 225 | exc_list=exc_list) | 278 | exc_list=exc_list) |
186 | 226 | remotly_addressable = [] | 279 | |
187 | 227 | for address in addresses: | 280 | if addresses: |
188 | 228 | if not address.startswith('fe80'): | 281 | global_addrs = [] |
189 | 229 | remotly_addressable.append(address) | 282 | for addr in addresses: |
190 | 230 | if fatal and not remotly_addressable: | 283 | key_scope_link_local = re.compile("^fe80::..(.+)%(.+)") |
191 | 231 | raise Exception("Interface '%s' doesn't have global ipv6 address." % iface) | 284 | m = re.match(key_scope_link_local, addr) |
192 | 232 | return remotly_addressable | 285 | if m: |
193 | 286 | eui_64_mac = m.group(1) | ||
194 | 287 | iface = m.group(2) | ||
195 | 288 | else: | ||
196 | 289 | global_addrs.append(addr) | ||
197 | 290 | |||
198 | 291 | if global_addrs: | ||
199 | 292 | # Make sure any found global addresses are not temporary | ||
200 | 293 | cmd = ['ip', 'addr', 'show', iface] | ||
201 | 294 | out = subprocess.check_output(cmd) | ||
202 | 295 | if dynamic_only: | ||
203 | 296 | key = re.compile("inet6 (.+)/[0-9]+ scope global dynamic.*") | ||
204 | 297 | else: | ||
205 | 298 | key = re.compile("inet6 (.+)/[0-9]+ scope global.*") | ||
206 | 299 | |||
207 | 300 | addrs = [] | ||
208 | 301 | for line in out.split('\n'): | ||
209 | 302 | line = line.strip() | ||
210 | 303 | m = re.match(key, line) | ||
211 | 304 | if m and 'temporary' not in line: | ||
212 | 305 | # Return the first valid address we find | ||
213 | 306 | for addr in global_addrs: | ||
214 | 307 | if m.group(1) == addr: | ||
215 | 308 | if not dynamic_only or \ | ||
216 | 309 | m.group(1).endswith(eui_64_mac): | ||
217 | 310 | addrs.append(addr) | ||
218 | 311 | |||
219 | 312 | if addrs: | ||
220 | 313 | return addrs | ||
221 | 314 | |||
222 | 315 | if fatal: | ||
223 | 316 | raise Exception("Interface '%s' doesn't have a scope global " | ||
224 | 317 | "non-temporary ipv6 address." % iface) | ||
225 | 318 | |||
226 | 319 | return [] | ||
227 | 233 | 320 | ||
228 | 234 | 321 | ||
229 | 235 | def get_bridges(vnic_dir='/sys/devices/virtual/net'): | 322 | def get_bridges(vnic_dir='/sys/devices/virtual/net'): |
230 | 236 | 323 | ||
231 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
232 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-09-25 15:37:05 +0000 | |||
233 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-10-07 21:21:43 +0000 | |||
234 | @@ -1,6 +1,3 @@ | |||
235 | 1 | from bzrlib.branch import Branch | ||
236 | 2 | import os | ||
237 | 3 | import re | ||
238 | 4 | from charmhelpers.contrib.amulet.deployment import ( | 1 | from charmhelpers.contrib.amulet.deployment import ( |
239 | 5 | AmuletDeployment | 2 | AmuletDeployment |
240 | 6 | ) | 3 | ) |
241 | @@ -13,62 +10,62 @@ | |||
242 | 13 | that is specifically for use by OpenStack charms. | 10 | that is specifically for use by OpenStack charms. |
243 | 14 | """ | 11 | """ |
244 | 15 | 12 | ||
246 | 16 | def __init__(self, series=None, openstack=None, source=None): | 13 | def __init__(self, series=None, openstack=None, source=None, stable=True): |
247 | 17 | """Initialize the deployment environment.""" | 14 | """Initialize the deployment environment.""" |
248 | 18 | super(OpenStackAmuletDeployment, self).__init__(series) | 15 | super(OpenStackAmuletDeployment, self).__init__(series) |
249 | 19 | self.openstack = openstack | 16 | self.openstack = openstack |
250 | 20 | self.source = source | 17 | self.source = source |
261 | 21 | 18 | self.stable = stable | |
262 | 22 | def _is_dev_branch(self): | 19 | # Note(coreycb): this needs to be changed when new next branches come |
263 | 23 | """Determine if branch being tested is a dev (i.e. next) branch.""" | 20 | # out. |
264 | 24 | branch = Branch.open(os.getcwd()) | 21 | self.current_next = "trusty" |
255 | 25 | parent = branch.get_parent() | ||
256 | 26 | pattern = re.compile("^.*/next/$") | ||
257 | 27 | if (pattern.match(parent)): | ||
258 | 28 | return True | ||
259 | 29 | else: | ||
260 | 30 | return False | ||
265 | 31 | 22 | ||
266 | 32 | def _determine_branch_locations(self, other_services): | 23 | def _determine_branch_locations(self, other_services): |
267 | 33 | """Determine the branch locations for the other services. | 24 | """Determine the branch locations for the other services. |
268 | 34 | 25 | ||
278 | 35 | If the branch being tested is a dev branch, then determine the | 26 | Determine if the local branch being tested is derived from its |
279 | 36 | development branch locations for the other services. Otherwise, | 27 | stable or next (dev) branch, and based on this, use the corresonding |
280 | 37 | the default charm store branches will be used.""" | 28 | stable or next branches for the other_services.""" |
281 | 38 | name = 0 | 29 | base_charms = ['mysql', 'mongodb', 'rabbitmq-server'] |
282 | 39 | if self._is_dev_branch(): | 30 | |
283 | 40 | updated_services = [] | 31 | if self.stable: |
284 | 41 | for svc in other_services: | 32 | for svc in other_services: |
285 | 42 | if svc[name] in ['mysql', 'mongodb', 'rabbitmq-server']: | 33 | temp = 'lp:charms/{}' |
286 | 43 | location = 'lp:charms/{}'.format(svc[name]) | 34 | svc['location'] = temp.format(svc['name']) |
287 | 35 | else: | ||
288 | 36 | for svc in other_services: | ||
289 | 37 | if svc['name'] in base_charms: | ||
290 | 38 | temp = 'lp:charms/{}' | ||
291 | 39 | svc['location'] = temp.format(svc['name']) | ||
292 | 44 | else: | 40 | else: |
297 | 45 | temp = 'lp:~openstack-charmers/charms/trusty/{}/next' | 41 | temp = 'lp:~openstack-charmers/charms/{}/{}/next' |
298 | 46 | location = temp.format(svc[name]) | 42 | svc['location'] = temp.format(self.current_next, |
299 | 47 | updated_services.append(svc + (location,)) | 43 | svc['name']) |
296 | 48 | other_services = updated_services | ||
300 | 49 | return other_services | 44 | return other_services |
301 | 50 | 45 | ||
302 | 51 | def _add_services(self, this_service, other_services): | 46 | def _add_services(self, this_service, other_services): |
303 | 52 | """Add services to the deployment and set openstack-origin/source.""" | 47 | """Add services to the deployment and set openstack-origin/source.""" |
304 | 53 | name = 0 | ||
305 | 54 | other_services = self._determine_branch_locations(other_services) | 48 | other_services = self._determine_branch_locations(other_services) |
306 | 49 | |||
307 | 55 | super(OpenStackAmuletDeployment, self)._add_services(this_service, | 50 | super(OpenStackAmuletDeployment, self)._add_services(this_service, |
308 | 56 | other_services) | 51 | other_services) |
309 | 52 | |||
310 | 57 | services = other_services | 53 | services = other_services |
311 | 58 | services.append(this_service) | 54 | services.append(this_service) |
313 | 59 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph'] | 55 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
314 | 56 | 'ceph-osd', 'ceph-radosgw'] | ||
315 | 60 | 57 | ||
316 | 61 | if self.openstack: | 58 | if self.openstack: |
317 | 62 | for svc in services: | 59 | for svc in services: |
319 | 63 | if svc[name] not in use_source: | 60 | if svc['name'] not in use_source: |
320 | 64 | config = {'openstack-origin': self.openstack} | 61 | config = {'openstack-origin': self.openstack} |
322 | 65 | self.d.configure(svc[name], config) | 62 | self.d.configure(svc['name'], config) |
323 | 66 | 63 | ||
324 | 67 | if self.source: | 64 | if self.source: |
325 | 68 | for svc in services: | 65 | for svc in services: |
327 | 69 | if svc[name] in use_source: | 66 | if svc['name'] in use_source: |
328 | 70 | config = {'source': self.source} | 67 | config = {'source': self.source} |
330 | 71 | self.d.configure(svc[name], config) | 68 | self.d.configure(svc['name'], config) |
331 | 72 | 69 | ||
332 | 73 | def _configure_services(self, configs): | 70 | def _configure_services(self, configs): |
333 | 74 | """Configure all of the services.""" | 71 | """Configure all of the services.""" |
334 | 75 | 72 | ||
335 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' | |||
336 | --- hooks/charmhelpers/contrib/openstack/context.py 2014-09-25 15:37:05 +0000 | |||
337 | +++ hooks/charmhelpers/contrib/openstack/context.py 2014-10-07 21:21:43 +0000 | |||
338 | @@ -8,7 +8,6 @@ | |||
339 | 8 | check_call | 8 | check_call |
340 | 9 | ) | 9 | ) |
341 | 10 | 10 | ||
342 | 11 | |||
343 | 12 | from charmhelpers.fetch import ( | 11 | from charmhelpers.fetch import ( |
344 | 13 | apt_install, | 12 | apt_install, |
345 | 14 | filter_installed_packages, | 13 | filter_installed_packages, |
346 | @@ -28,6 +27,11 @@ | |||
347 | 28 | INFO | 27 | INFO |
348 | 29 | ) | 28 | ) |
349 | 30 | 29 | ||
350 | 30 | from charmhelpers.core.host import ( | ||
351 | 31 | mkdir, | ||
352 | 32 | write_file | ||
353 | 33 | ) | ||
354 | 34 | |||
355 | 31 | from charmhelpers.contrib.hahelpers.cluster import ( | 35 | from charmhelpers.contrib.hahelpers.cluster import ( |
356 | 32 | determine_apache_port, | 36 | determine_apache_port, |
357 | 33 | determine_api_port, | 37 | determine_api_port, |
358 | @@ -38,6 +42,7 @@ | |||
359 | 38 | from charmhelpers.contrib.hahelpers.apache import ( | 42 | from charmhelpers.contrib.hahelpers.apache import ( |
360 | 39 | get_cert, | 43 | get_cert, |
361 | 40 | get_ca_cert, | 44 | get_ca_cert, |
362 | 45 | install_ca_cert, | ||
363 | 41 | ) | 46 | ) |
364 | 42 | 47 | ||
365 | 43 | from charmhelpers.contrib.openstack.neutron import ( | 48 | from charmhelpers.contrib.openstack.neutron import ( |
366 | @@ -47,8 +52,13 @@ | |||
367 | 47 | from charmhelpers.contrib.network.ip import ( | 52 | from charmhelpers.contrib.network.ip import ( |
368 | 48 | get_address_in_network, | 53 | get_address_in_network, |
369 | 49 | get_ipv6_addr, | 54 | get_ipv6_addr, |
370 | 55 | get_netmask_for_address, | ||
371 | 56 | format_ipv6_addr, | ||
372 | 57 | is_address_in_network | ||
373 | 50 | ) | 58 | ) |
374 | 51 | 59 | ||
375 | 60 | from charmhelpers.contrib.openstack.utils import get_host_ip | ||
376 | 61 | |||
377 | 52 | CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' | 62 | CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' |
378 | 53 | 63 | ||
379 | 54 | 64 | ||
380 | @@ -168,8 +178,10 @@ | |||
381 | 168 | for rid in relation_ids('shared-db'): | 178 | for rid in relation_ids('shared-db'): |
382 | 169 | for unit in related_units(rid): | 179 | for unit in related_units(rid): |
383 | 170 | rdata = relation_get(rid=rid, unit=unit) | 180 | rdata = relation_get(rid=rid, unit=unit) |
384 | 181 | host = rdata.get('db_host') | ||
385 | 182 | host = format_ipv6_addr(host) or host | ||
386 | 171 | ctxt = { | 183 | ctxt = { |
388 | 172 | 'database_host': rdata.get('db_host'), | 184 | 'database_host': host, |
389 | 173 | 'database': self.database, | 185 | 'database': self.database, |
390 | 174 | 'database_user': self.user, | 186 | 'database_user': self.user, |
391 | 175 | 'database_password': rdata.get(password_setting), | 187 | 'database_password': rdata.get(password_setting), |
392 | @@ -245,10 +257,15 @@ | |||
393 | 245 | for rid in relation_ids('identity-service'): | 257 | for rid in relation_ids('identity-service'): |
394 | 246 | for unit in related_units(rid): | 258 | for unit in related_units(rid): |
395 | 247 | rdata = relation_get(rid=rid, unit=unit) | 259 | rdata = relation_get(rid=rid, unit=unit) |
396 | 260 | serv_host = rdata.get('service_host') | ||
397 | 261 | serv_host = format_ipv6_addr(serv_host) or serv_host | ||
398 | 262 | auth_host = rdata.get('auth_host') | ||
399 | 263 | auth_host = format_ipv6_addr(auth_host) or auth_host | ||
400 | 264 | |||
401 | 248 | ctxt = { | 265 | ctxt = { |
402 | 249 | 'service_port': rdata.get('service_port'), | 266 | 'service_port': rdata.get('service_port'), |
405 | 250 | 'service_host': rdata.get('service_host'), | 267 | 'service_host': serv_host, |
406 | 251 | 'auth_host': rdata.get('auth_host'), | 268 | 'auth_host': auth_host, |
407 | 252 | 'auth_port': rdata.get('auth_port'), | 269 | 'auth_port': rdata.get('auth_port'), |
408 | 253 | 'admin_tenant_name': rdata.get('service_tenant'), | 270 | 'admin_tenant_name': rdata.get('service_tenant'), |
409 | 254 | 'admin_user': rdata.get('service_username'), | 271 | 'admin_user': rdata.get('service_username'), |
410 | @@ -297,11 +314,13 @@ | |||
411 | 297 | for unit in related_units(rid): | 314 | for unit in related_units(rid): |
412 | 298 | if relation_get('clustered', rid=rid, unit=unit): | 315 | if relation_get('clustered', rid=rid, unit=unit): |
413 | 299 | ctxt['clustered'] = True | 316 | ctxt['clustered'] = True |
416 | 300 | ctxt['rabbitmq_host'] = relation_get('vip', rid=rid, | 317 | vip = relation_get('vip', rid=rid, unit=unit) |
417 | 301 | unit=unit) | 318 | vip = format_ipv6_addr(vip) or vip |
418 | 319 | ctxt['rabbitmq_host'] = vip | ||
419 | 302 | else: | 320 | else: |
422 | 303 | ctxt['rabbitmq_host'] = relation_get('private-address', | 321 | host = relation_get('private-address', rid=rid, unit=unit) |
423 | 304 | rid=rid, unit=unit) | 322 | host = format_ipv6_addr(host) or host |
424 | 323 | ctxt['rabbitmq_host'] = host | ||
425 | 305 | ctxt.update({ | 324 | ctxt.update({ |
426 | 306 | 'rabbitmq_user': username, | 325 | 'rabbitmq_user': username, |
427 | 307 | 'rabbitmq_password': relation_get('password', rid=rid, | 326 | 'rabbitmq_password': relation_get('password', rid=rid, |
428 | @@ -340,8 +359,9 @@ | |||
429 | 340 | and len(related_units(rid)) > 1: | 359 | and len(related_units(rid)) > 1: |
430 | 341 | rabbitmq_hosts = [] | 360 | rabbitmq_hosts = [] |
431 | 342 | for unit in related_units(rid): | 361 | for unit in related_units(rid): |
434 | 343 | rabbitmq_hosts.append(relation_get('private-address', | 362 | host = relation_get('private-address', rid=rid, unit=unit) |
435 | 344 | rid=rid, unit=unit)) | 363 | host = format_ipv6_addr(host) or host |
436 | 364 | rabbitmq_hosts.append(host) | ||
437 | 345 | ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts) | 365 | ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts) |
438 | 346 | if not context_complete(ctxt): | 366 | if not context_complete(ctxt): |
439 | 347 | return {} | 367 | return {} |
440 | @@ -370,6 +390,7 @@ | |||
441 | 370 | ceph_addr = \ | 390 | ceph_addr = \ |
442 | 371 | relation_get('ceph-public-address', rid=rid, unit=unit) or \ | 391 | relation_get('ceph-public-address', rid=rid, unit=unit) or \ |
443 | 372 | relation_get('private-address', rid=rid, unit=unit) | 392 | relation_get('private-address', rid=rid, unit=unit) |
444 | 393 | ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr | ||
445 | 373 | mon_hosts.append(ceph_addr) | 394 | mon_hosts.append(ceph_addr) |
446 | 374 | 395 | ||
447 | 375 | ctxt = { | 396 | ctxt = { |
448 | @@ -390,6 +411,9 @@ | |||
449 | 390 | return ctxt | 411 | return ctxt |
450 | 391 | 412 | ||
451 | 392 | 413 | ||
452 | 414 | ADDRESS_TYPES = ['admin', 'internal', 'public'] | ||
453 | 415 | |||
454 | 416 | |||
455 | 393 | class HAProxyContext(OSContextGenerator): | 417 | class HAProxyContext(OSContextGenerator): |
456 | 394 | interfaces = ['cluster'] | 418 | interfaces = ['cluster'] |
457 | 395 | 419 | ||
458 | @@ -402,29 +426,62 @@ | |||
459 | 402 | if not relation_ids('cluster'): | 426 | if not relation_ids('cluster'): |
460 | 403 | return {} | 427 | return {} |
461 | 404 | 428 | ||
462 | 429 | l_unit = local_unit().replace('/', '-') | ||
463 | 430 | |||
464 | 431 | if config('prefer-ipv6'): | ||
465 | 432 | addr = get_ipv6_addr(exc_list=[config('vip')])[0] | ||
466 | 433 | else: | ||
467 | 434 | addr = get_host_ip(unit_get('private-address')) | ||
468 | 435 | |||
469 | 405 | cluster_hosts = {} | 436 | cluster_hosts = {} |
483 | 406 | l_unit = local_unit().replace('/', '-') | 437 | |
484 | 407 | if config('prefer-ipv6'): | 438 | # NOTE(jamespage): build out map of configured network endpoints |
485 | 408 | addr = get_ipv6_addr() | 439 | # and associated backends |
486 | 409 | else: | 440 | for addr_type in ADDRESS_TYPES: |
487 | 410 | addr = unit_get('private-address') | 441 | laddr = get_address_in_network( |
488 | 411 | cluster_hosts[l_unit] = get_address_in_network(config('os-internal-network'), | 442 | config('os-{}-network'.format(addr_type))) |
489 | 412 | addr) | 443 | if laddr: |
490 | 413 | 444 | cluster_hosts[laddr] = {} | |
491 | 414 | for rid in relation_ids('cluster'): | 445 | cluster_hosts[laddr]['network'] = "{}/{}".format( |
492 | 415 | for unit in related_units(rid): | 446 | laddr, |
493 | 416 | _unit = unit.replace('/', '-') | 447 | get_netmask_for_address(laddr) |
494 | 417 | addr = relation_get('private-address', rid=rid, unit=unit) | 448 | ) |
495 | 418 | cluster_hosts[_unit] = addr | 449 | cluster_hosts[laddr]['backends'] = {} |
496 | 450 | cluster_hosts[laddr]['backends'][l_unit] = laddr | ||
497 | 451 | for rid in relation_ids('cluster'): | ||
498 | 452 | for unit in related_units(rid): | ||
499 | 453 | _unit = unit.replace('/', '-') | ||
500 | 454 | _laddr = relation_get('{}-address'.format(addr_type), | ||
501 | 455 | rid=rid, unit=unit) | ||
502 | 456 | if _laddr: | ||
503 | 457 | cluster_hosts[laddr]['backends'][_unit] = _laddr | ||
504 | 458 | |||
505 | 459 | # NOTE(jamespage) no split configurations found, just use | ||
506 | 460 | # private addresses | ||
507 | 461 | if not cluster_hosts: | ||
508 | 462 | cluster_hosts[addr] = {} | ||
509 | 463 | cluster_hosts[addr]['network'] = "{}/{}".format( | ||
510 | 464 | addr, | ||
511 | 465 | get_netmask_for_address(addr) | ||
512 | 466 | ) | ||
513 | 467 | cluster_hosts[addr]['backends'] = {} | ||
514 | 468 | cluster_hosts[addr]['backends'][l_unit] = addr | ||
515 | 469 | for rid in relation_ids('cluster'): | ||
516 | 470 | for unit in related_units(rid): | ||
517 | 471 | _unit = unit.replace('/', '-') | ||
518 | 472 | _laddr = relation_get('private-address', | ||
519 | 473 | rid=rid, unit=unit) | ||
520 | 474 | if _laddr: | ||
521 | 475 | cluster_hosts[addr]['backends'][_unit] = _laddr | ||
522 | 419 | 476 | ||
523 | 420 | ctxt = { | 477 | ctxt = { |
525 | 421 | 'units': cluster_hosts, | 478 | 'frontends': cluster_hosts, |
526 | 422 | } | 479 | } |
527 | 423 | 480 | ||
528 | 424 | if config('haproxy-server-timeout'): | 481 | if config('haproxy-server-timeout'): |
530 | 425 | ctxt['haproxy-server-timeout'] = config('haproxy-server-timeout') | 482 | ctxt['haproxy_server_timeout'] = config('haproxy-server-timeout') |
531 | 426 | if config('haproxy-client-timeout'): | 483 | if config('haproxy-client-timeout'): |
533 | 427 | ctxt['haproxy-client-timeout'] = config('haproxy-client-timeout') | 484 | ctxt['haproxy_client_timeout'] = config('haproxy-client-timeout') |
534 | 428 | 485 | ||
535 | 429 | if config('prefer-ipv6'): | 486 | if config('prefer-ipv6'): |
536 | 430 | ctxt['local_host'] = 'ip6-localhost' | 487 | ctxt['local_host'] = 'ip6-localhost' |
537 | @@ -435,12 +492,13 @@ | |||
538 | 435 | ctxt['haproxy_host'] = '0.0.0.0' | 492 | ctxt['haproxy_host'] = '0.0.0.0' |
539 | 436 | ctxt['stat_port'] = ':8888' | 493 | ctxt['stat_port'] = ':8888' |
540 | 437 | 494 | ||
547 | 438 | if len(cluster_hosts.keys()) > 1: | 495 | for frontend in cluster_hosts: |
548 | 439 | # Enable haproxy when we have enough peers. | 496 | if len(cluster_hosts[frontend]['backends']) > 1: |
549 | 440 | log('Ensuring haproxy enabled in /etc/default/haproxy.') | 497 | # Enable haproxy when we have enough peers. |
550 | 441 | with open('/etc/default/haproxy', 'w') as out: | 498 | log('Ensuring haproxy enabled in /etc/default/haproxy.') |
551 | 442 | out.write('ENABLED=1\n') | 499 | with open('/etc/default/haproxy', 'w') as out: |
552 | 443 | return ctxt | 500 | out.write('ENABLED=1\n') |
553 | 501 | return ctxt | ||
554 | 444 | log('HAProxy context is incomplete, this unit has no peers.') | 502 | log('HAProxy context is incomplete, this unit has no peers.') |
555 | 445 | return {} | 503 | return {} |
556 | 446 | 504 | ||
557 | @@ -495,22 +553,36 @@ | |||
558 | 495 | cmd = ['a2enmod', 'ssl', 'proxy', 'proxy_http'] | 553 | cmd = ['a2enmod', 'ssl', 'proxy', 'proxy_http'] |
559 | 496 | check_call(cmd) | 554 | check_call(cmd) |
560 | 497 | 555 | ||
564 | 498 | def configure_cert(self): | 556 | def configure_cert(self, cn=None): |
562 | 499 | if not os.path.isdir('/etc/apache2/ssl'): | ||
563 | 500 | os.mkdir('/etc/apache2/ssl') | ||
565 | 501 | ssl_dir = os.path.join('/etc/apache2/ssl/', self.service_namespace) | 557 | ssl_dir = os.path.join('/etc/apache2/ssl/', self.service_namespace) |
573 | 502 | if not os.path.isdir(ssl_dir): | 558 | mkdir(path=ssl_dir) |
574 | 503 | os.mkdir(ssl_dir) | 559 | cert, key = get_cert(cn) |
575 | 504 | cert, key = get_cert() | 560 | if cn: |
576 | 505 | with open(os.path.join(ssl_dir, 'cert'), 'w') as cert_out: | 561 | cert_filename = 'cert_{}'.format(cn) |
577 | 506 | cert_out.write(b64decode(cert)) | 562 | key_filename = 'key_{}'.format(cn) |
578 | 507 | with open(os.path.join(ssl_dir, 'key'), 'w') as key_out: | 563 | else: |
579 | 508 | key_out.write(b64decode(key)) | 564 | cert_filename = 'cert' |
580 | 565 | key_filename = 'key' | ||
581 | 566 | write_file(path=os.path.join(ssl_dir, cert_filename), | ||
582 | 567 | content=b64decode(cert)) | ||
583 | 568 | write_file(path=os.path.join(ssl_dir, key_filename), | ||
584 | 569 | content=b64decode(key)) | ||
585 | 570 | |||
586 | 571 | def configure_ca(self): | ||
587 | 509 | ca_cert = get_ca_cert() | 572 | ca_cert = get_ca_cert() |
588 | 510 | if ca_cert: | 573 | if ca_cert: |
592 | 511 | with open(CA_CERT_PATH, 'w') as ca_out: | 574 | install_ca_cert(b64decode(ca_cert)) |
593 | 512 | ca_out.write(b64decode(ca_cert)) | 575 | |
594 | 513 | check_call(['update-ca-certificates']) | 576 | def canonical_names(self): |
595 | 577 | '''Figure out which canonical names clients will access this service''' | ||
596 | 578 | cns = [] | ||
597 | 579 | for r_id in relation_ids('identity-service'): | ||
598 | 580 | for unit in related_units(r_id): | ||
599 | 581 | rdata = relation_get(rid=r_id, unit=unit) | ||
600 | 582 | for k in rdata: | ||
601 | 583 | if k.startswith('ssl_key_'): | ||
602 | 584 | cns.append(k.lstrip('ssl_key_')) | ||
603 | 585 | return list(set(cns)) | ||
604 | 514 | 586 | ||
605 | 515 | def __call__(self): | 587 | def __call__(self): |
606 | 516 | if isinstance(self.external_ports, basestring): | 588 | if isinstance(self.external_ports, basestring): |
607 | @@ -518,21 +590,47 @@ | |||
608 | 518 | if (not self.external_ports or not https()): | 590 | if (not self.external_ports or not https()): |
609 | 519 | return {} | 591 | return {} |
610 | 520 | 592 | ||
612 | 521 | self.configure_cert() | 593 | self.configure_ca() |
613 | 522 | self.enable_modules() | 594 | self.enable_modules() |
614 | 523 | 595 | ||
615 | 524 | ctxt = { | 596 | ctxt = { |
616 | 525 | 'namespace': self.service_namespace, | 597 | 'namespace': self.service_namespace, |
619 | 526 | 'private_address': unit_get('private-address'), | 598 | 'endpoints': [], |
620 | 527 | 'endpoints': [] | 599 | 'ext_ports': [] |
621 | 528 | } | 600 | } |
629 | 529 | if is_clustered(): | 601 | |
630 | 530 | ctxt['private_address'] = config('vip') | 602 | for cn in self.canonical_names(): |
631 | 531 | for api_port in self.external_ports: | 603 | self.configure_cert(cn) |
632 | 532 | ext_port = determine_apache_port(api_port) | 604 | |
633 | 533 | int_port = determine_api_port(api_port) | 605 | addresses = [] |
634 | 534 | portmap = (int(ext_port), int(int_port)) | 606 | vips = [] |
635 | 535 | ctxt['endpoints'].append(portmap) | 607 | if config('vip'): |
636 | 608 | vips = config('vip').split() | ||
637 | 609 | |||
638 | 610 | for network_type in ['os-internal-network', | ||
639 | 611 | 'os-admin-network', | ||
640 | 612 | 'os-public-network']: | ||
641 | 613 | address = get_address_in_network(config(network_type), | ||
642 | 614 | unit_get('private-address')) | ||
643 | 615 | if len(vips) > 0 and is_clustered(): | ||
644 | 616 | for vip in vips: | ||
645 | 617 | if is_address_in_network(config(network_type), | ||
646 | 618 | vip): | ||
647 | 619 | addresses.append((address, vip)) | ||
648 | 620 | break | ||
649 | 621 | elif is_clustered(): | ||
650 | 622 | addresses.append((address, config('vip'))) | ||
651 | 623 | else: | ||
652 | 624 | addresses.append((address, address)) | ||
653 | 625 | |||
654 | 626 | for address, endpoint in set(addresses): | ||
655 | 627 | for api_port in self.external_ports: | ||
656 | 628 | ext_port = determine_apache_port(api_port) | ||
657 | 629 | int_port = determine_api_port(api_port) | ||
658 | 630 | portmap = (address, endpoint, int(ext_port), int(int_port)) | ||
659 | 631 | ctxt['endpoints'].append(portmap) | ||
660 | 632 | ctxt['ext_ports'].append(int(ext_port)) | ||
661 | 633 | ctxt['ext_ports'] = list(set(ctxt['ext_ports'])) | ||
662 | 536 | return ctxt | 634 | return ctxt |
663 | 537 | 635 | ||
664 | 538 | 636 | ||
665 | @@ -662,22 +760,22 @@ | |||
666 | 662 | 760 | ||
667 | 663 | class OSConfigFlagContext(OSContextGenerator): | 761 | class OSConfigFlagContext(OSContextGenerator): |
668 | 664 | 762 | ||
685 | 665 | """ | 763 | """ |
686 | 666 | Responsible for adding user-defined config-flags in charm config to a | 764 | Responsible for adding user-defined config-flags in charm config to a |
687 | 667 | template context. | 765 | template context. |
688 | 668 | 766 | ||
689 | 669 | NOTE: the value of config-flags may be a comma-separated list of | 767 | NOTE: the value of config-flags may be a comma-separated list of |
690 | 670 | key=value pairs and some Openstack config files support | 768 | key=value pairs and some Openstack config files support |
691 | 671 | comma-separated lists as values. | 769 | comma-separated lists as values. |
692 | 672 | """ | 770 | """ |
693 | 673 | 771 | ||
694 | 674 | def __call__(self): | 772 | def __call__(self): |
695 | 675 | config_flags = config('config-flags') | 773 | config_flags = config('config-flags') |
696 | 676 | if not config_flags: | 774 | if not config_flags: |
697 | 677 | return {} | 775 | return {} |
698 | 678 | 776 | ||
699 | 679 | flags = config_flags_parser(config_flags) | 777 | flags = config_flags_parser(config_flags) |
700 | 680 | return {'user_config_flags': flags} | 778 | return {'user_config_flags': flags} |
701 | 681 | 779 | ||
702 | 682 | 780 | ||
703 | 683 | class SubordinateConfigContext(OSContextGenerator): | 781 | class SubordinateConfigContext(OSContextGenerator): |
704 | @@ -792,3 +890,35 @@ | |||
705 | 792 | 'use_syslog': config('use-syslog') | 890 | 'use_syslog': config('use-syslog') |
706 | 793 | } | 891 | } |
707 | 794 | return ctxt | 892 | return ctxt |
708 | 893 | |||
709 | 894 | |||
710 | 895 | class BindHostContext(OSContextGenerator): | ||
711 | 896 | |||
712 | 897 | def __call__(self): | ||
713 | 898 | if config('prefer-ipv6'): | ||
714 | 899 | return { | ||
715 | 900 | 'bind_host': '::' | ||
716 | 901 | } | ||
717 | 902 | else: | ||
718 | 903 | return { | ||
719 | 904 | 'bind_host': '0.0.0.0' | ||
720 | 905 | } | ||
721 | 906 | |||
722 | 907 | |||
723 | 908 | class WorkerConfigContext(OSContextGenerator): | ||
724 | 909 | |||
725 | 910 | @property | ||
726 | 911 | def num_cpus(self): | ||
727 | 912 | try: | ||
728 | 913 | from psutil import NUM_CPUS | ||
729 | 914 | except ImportError: | ||
730 | 915 | apt_install('python-psutil', fatal=True) | ||
731 | 916 | from psutil import NUM_CPUS | ||
732 | 917 | return NUM_CPUS | ||
733 | 918 | |||
734 | 919 | def __call__(self): | ||
735 | 920 | multiplier = config('worker-multiplier') or 1 | ||
736 | 921 | ctxt = { | ||
737 | 922 | "workers": self.num_cpus * multiplier | ||
738 | 923 | } | ||
739 | 924 | return ctxt | ||
740 | 795 | 925 | ||
741 | === modified file 'hooks/charmhelpers/contrib/openstack/ip.py' | |||
742 | --- hooks/charmhelpers/contrib/openstack/ip.py 2014-08-13 13:12:47 +0000 | |||
743 | +++ hooks/charmhelpers/contrib/openstack/ip.py 2014-10-07 21:21:43 +0000 | |||
744 | @@ -66,7 +66,7 @@ | |||
745 | 66 | resolved_address = vip | 66 | resolved_address = vip |
746 | 67 | else: | 67 | else: |
747 | 68 | if config('prefer-ipv6'): | 68 | if config('prefer-ipv6'): |
749 | 69 | fallback_addr = get_ipv6_addr() | 69 | fallback_addr = get_ipv6_addr(exc_list=[config('vip')])[0] |
750 | 70 | else: | 70 | else: |
751 | 71 | fallback_addr = unit_get(_address_map[endpoint_type]['fallback']) | 71 | fallback_addr = unit_get(_address_map[endpoint_type]['fallback']) |
752 | 72 | resolved_address = get_address_in_network( | 72 | resolved_address = get_address_in_network( |
753 | 73 | 73 | ||
754 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
755 | --- hooks/charmhelpers/contrib/openstack/utils.py 2014-09-17 10:33:02 +0000 | |||
756 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2014-10-07 21:21:43 +0000 | |||
757 | @@ -4,6 +4,7 @@ | |||
758 | 4 | from collections import OrderedDict | 4 | from collections import OrderedDict |
759 | 5 | 5 | ||
760 | 6 | import subprocess | 6 | import subprocess |
761 | 7 | import json | ||
762 | 7 | import os | 8 | import os |
763 | 8 | import socket | 9 | import socket |
764 | 9 | import sys | 10 | import sys |
765 | @@ -13,7 +14,9 @@ | |||
766 | 13 | log as juju_log, | 14 | log as juju_log, |
767 | 14 | charm_dir, | 15 | charm_dir, |
768 | 15 | ERROR, | 16 | ERROR, |
770 | 16 | INFO | 17 | INFO, |
771 | 18 | relation_ids, | ||
772 | 19 | relation_set | ||
773 | 17 | ) | 20 | ) |
774 | 18 | 21 | ||
775 | 19 | from charmhelpers.contrib.storage.linux.lvm import ( | 22 | from charmhelpers.contrib.storage.linux.lvm import ( |
776 | @@ -22,6 +25,10 @@ | |||
777 | 22 | remove_lvm_physical_volume, | 25 | remove_lvm_physical_volume, |
778 | 23 | ) | 26 | ) |
779 | 24 | 27 | ||
780 | 28 | from charmhelpers.contrib.network.ip import ( | ||
781 | 29 | get_ipv6_addr | ||
782 | 30 | ) | ||
783 | 31 | |||
784 | 25 | from charmhelpers.core.host import lsb_release, mounts, umount | 32 | from charmhelpers.core.host import lsb_release, mounts, umount |
785 | 26 | from charmhelpers.fetch import apt_install, apt_cache | 33 | from charmhelpers.fetch import apt_install, apt_cache |
786 | 27 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk | 34 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk |
787 | @@ -71,6 +78,8 @@ | |||
788 | 71 | ('1.12.0', 'icehouse'), | 78 | ('1.12.0', 'icehouse'), |
789 | 72 | ('1.11.0', 'icehouse'), | 79 | ('1.11.0', 'icehouse'), |
790 | 73 | ('2.0.0', 'juno'), | 80 | ('2.0.0', 'juno'), |
791 | 81 | ('2.1.0', 'juno'), | ||
792 | 82 | ('2.2.0', 'juno'), | ||
793 | 74 | ]) | 83 | ]) |
794 | 75 | 84 | ||
795 | 76 | DEFAULT_LOOPBACK_SIZE = '5G' | 85 | DEFAULT_LOOPBACK_SIZE = '5G' |
796 | @@ -457,3 +466,21 @@ | |||
797 | 457 | return result | 466 | return result |
798 | 458 | else: | 467 | else: |
799 | 459 | return result.split('.')[0] | 468 | return result.split('.')[0] |
800 | 469 | |||
801 | 470 | |||
802 | 471 | def sync_db_with_multi_ipv6_addresses(database, database_user, | ||
803 | 472 | relation_prefix=None): | ||
804 | 473 | hosts = get_ipv6_addr(dynamic_only=False) | ||
805 | 474 | |||
806 | 475 | kwargs = {'database': database, | ||
807 | 476 | 'username': database_user, | ||
808 | 477 | 'hostname': json.dumps(hosts)} | ||
809 | 478 | |||
810 | 479 | if relation_prefix: | ||
811 | 480 | keys = kwargs.keys() | ||
812 | 481 | for key in keys: | ||
813 | 482 | kwargs["%s_%s" % (relation_prefix, key)] = kwargs[key] | ||
814 | 483 | del kwargs[key] | ||
815 | 484 | |||
816 | 485 | for rid in relation_ids('shared-db'): | ||
817 | 486 | relation_set(relation_id=rid, **kwargs) | ||
818 | 460 | 487 | ||
819 | === added file 'hooks/charmhelpers/core/sysctl.py' | |||
820 | --- hooks/charmhelpers/core/sysctl.py 1970-01-01 00:00:00 +0000 | |||
821 | +++ hooks/charmhelpers/core/sysctl.py 2014-10-07 21:21:43 +0000 | |||
822 | @@ -0,0 +1,34 @@ | |||
823 | 1 | #!/usr/bin/env python | ||
824 | 2 | # -*- coding: utf-8 -*- | ||
825 | 3 | |||
826 | 4 | __author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' | ||
827 | 5 | |||
828 | 6 | import yaml | ||
829 | 7 | |||
830 | 8 | from subprocess import check_call | ||
831 | 9 | |||
832 | 10 | from charmhelpers.core.hookenv import ( | ||
833 | 11 | log, | ||
834 | 12 | DEBUG, | ||
835 | 13 | ) | ||
836 | 14 | |||
837 | 15 | |||
838 | 16 | def create(sysctl_dict, sysctl_file): | ||
839 | 17 | """Creates a sysctl.conf file from a YAML associative array | ||
840 | 18 | |||
841 | 19 | :param sysctl_dict: a dict of sysctl options eg { 'kernel.max_pid': 1337 } | ||
842 | 20 | :type sysctl_dict: dict | ||
843 | 21 | :param sysctl_file: path to the sysctl file to be saved | ||
844 | 22 | :type sysctl_file: str or unicode | ||
845 | 23 | :returns: None | ||
846 | 24 | """ | ||
847 | 25 | sysctl_dict = yaml.load(sysctl_dict) | ||
848 | 26 | |||
849 | 27 | with open(sysctl_file, "w") as fd: | ||
850 | 28 | for key, value in sysctl_dict.items(): | ||
851 | 29 | fd.write("{}={}\n".format(key, value)) | ||
852 | 30 | |||
853 | 31 | log("Updating sysctl_file: %s values: %s" % (sysctl_file, sysctl_dict), | ||
854 | 32 | level=DEBUG) | ||
855 | 33 | |||
856 | 34 | check_call(["sysctl", "-p", sysctl_file]) | ||
857 | 0 | 35 | ||
858 | === modified file 'tests/00-setup' | |||
859 | --- tests/00-setup 2014-07-17 15:16:21 +0000 | |||
860 | +++ tests/00-setup 2014-10-07 21:21:43 +0000 | |||
861 | @@ -4,7 +4,8 @@ | |||
862 | 4 | 4 | ||
863 | 5 | sudo add-apt-repository --yes ppa:juju/stable | 5 | sudo add-apt-repository --yes ppa:juju/stable |
864 | 6 | sudo apt-get update --yes | 6 | sudo apt-get update --yes |
869 | 7 | sudo apt-get install --yes python-amulet | 7 | sudo apt-get install --yes python-amulet \ |
870 | 8 | sudo apt-get install --yes python-neutronclient | 8 | python-neutronclient \ |
871 | 9 | sudo apt-get install --yes python-keystoneclient | 9 | python-keystoneclient \ |
872 | 10 | sudo apt-get install --yes python-novaclient | 10 | python-novaclient \ |
873 | 11 | python-glanceclient | ||
874 | 11 | 12 | ||
875 | === modified file 'tests/README' | |||
876 | --- tests/README 2014-07-17 15:16:21 +0000 | |||
877 | +++ tests/README 2014-10-07 21:21:43 +0000 | |||
878 | @@ -1,6 +1,12 @@ | |||
879 | 1 | This directory provides Amulet tests that focus on verification of | 1 | This directory provides Amulet tests that focus on verification of |
880 | 2 | quantum-gateway deployments. | 2 | quantum-gateway deployments. |
881 | 3 | 3 | ||
882 | 4 | In order to run tests, you'll need charm-tools installed (in addition to | ||
883 | 5 | juju, of course): | ||
884 | 6 | sudo add-apt-repository ppa:juju/stable | ||
885 | 7 | sudo apt-get update | ||
886 | 8 | sudo apt-get install charm-tools | ||
887 | 9 | |||
888 | 4 | If you use a web proxy server to access the web, you'll need to set the | 10 | If you use a web proxy server to access the web, you'll need to set the |
889 | 5 | AMULET_HTTP_PROXY environment variable to the http URL of the proxy server. | 11 | AMULET_HTTP_PROXY environment variable to the http URL of the proxy server. |
890 | 6 | 12 | ||
891 | 7 | 13 | ||
892 | === modified file 'tests/basic_deployment.py' | |||
893 | --- tests/basic_deployment.py 2014-09-08 19:02:06 +0000 | |||
894 | +++ tests/basic_deployment.py 2014-10-07 21:21:43 +0000 | |||
895 | @@ -1,6 +1,7 @@ | |||
896 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
897 | 2 | 2 | ||
898 | 3 | import amulet | 3 | import amulet |
899 | 4 | import time | ||
900 | 4 | try: | 5 | try: |
901 | 5 | from quantumclient.v2_0 import client as neutronclient | 6 | from quantumclient.v2_0 import client as neutronclient |
902 | 6 | except ImportError: | 7 | except ImportError: |
903 | @@ -23,10 +24,10 @@ | |||
904 | 23 | class QuantumGatewayBasicDeployment(OpenStackAmuletDeployment): | 24 | class QuantumGatewayBasicDeployment(OpenStackAmuletDeployment): |
905 | 24 | """Amulet tests on a basic quantum-gateway deployment.""" | 25 | """Amulet tests on a basic quantum-gateway deployment.""" |
906 | 25 | 26 | ||
908 | 26 | def __init__(self, series, openstack=None, source=None): | 27 | def __init__(self, series, openstack=None, source=None, stable=False): |
909 | 27 | """Deploy the entire test environment.""" | 28 | """Deploy the entire test environment.""" |
910 | 28 | super(QuantumGatewayBasicDeployment, self).__init__(series, openstack, | 29 | super(QuantumGatewayBasicDeployment, self).__init__(series, openstack, |
912 | 29 | source) | 30 | source, stable) |
913 | 30 | self._add_services() | 31 | self._add_services() |
914 | 31 | self._add_relations() | 32 | self._add_relations() |
915 | 32 | self._configure_services() | 33 | self._configure_services() |
916 | @@ -34,13 +35,16 @@ | |||
917 | 34 | self._initialize_tests() | 35 | self._initialize_tests() |
918 | 35 | 36 | ||
919 | 36 | def _add_services(self): | 37 | def _add_services(self): |
927 | 37 | """Add the service that we're testing, including the number of units, | 38 | """Add services |
928 | 38 | where quantum-gateway is local, and the other charms are from | 39 | |
929 | 39 | the charm store.""" | 40 | Add the services that we're testing, where quantum-gateway is local, |
930 | 40 | this_service = ('quantum-gateway', 1) | 41 | and the rest of the service are from lp branches that are |
931 | 41 | other_services = [('mysql', 1), | 42 | compatible with the local charm (e.g. stable or next). |
932 | 42 | ('rabbitmq-server', 1), ('keystone', 1), | 43 | """ |
933 | 43 | ('nova-cloud-controller', 1)] | 44 | this_service = {'name': 'quantum-gateway'} |
934 | 45 | other_services = [{'name': 'mysql'}, | ||
935 | 46 | {'name': 'rabbitmq-server'}, {'name': 'keystone'}, | ||
936 | 47 | {'name': 'nova-cloud-controller'}] | ||
937 | 44 | super(QuantumGatewayBasicDeployment, self)._add_services(this_service, | 48 | super(QuantumGatewayBasicDeployment, self)._add_services(this_service, |
938 | 45 | other_services) | 49 | other_services) |
939 | 46 | 50 | ||
940 | @@ -77,6 +81,9 @@ | |||
941 | 77 | self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0'] | 81 | self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0'] |
942 | 78 | self.quantum_gateway_sentry = self.d.sentry.unit['quantum-gateway/0'] | 82 | self.quantum_gateway_sentry = self.d.sentry.unit['quantum-gateway/0'] |
943 | 79 | 83 | ||
944 | 84 | # Let things settle a bit before moving forward | ||
945 | 85 | time.sleep(30) | ||
946 | 86 | |||
947 | 80 | # Authenticate admin with keystone | 87 | # Authenticate admin with keystone |
948 | 81 | self.keystone = u.authenticate_keystone_admin(self.keystone_sentry, | 88 | self.keystone = u.authenticate_keystone_admin(self.keystone_sentry, |
949 | 82 | user='admin', | 89 | user='admin', |
950 | @@ -238,9 +245,14 @@ | |||
951 | 238 | message = u.relation_error('nova-cc network-service', ret) | 245 | message = u.relation_error('nova-cc network-service', ret) |
952 | 239 | amulet.raise_status(amulet.FAIL, msg=message) | 246 | amulet.raise_status(amulet.FAIL, msg=message) |
953 | 240 | 247 | ||
955 | 241 | def test_restart_on_config_change(self): | 248 | def test_z_restart_on_config_change(self): |
956 | 242 | """Verify that the specified services are restarted when the config | 249 | """Verify that the specified services are restarted when the config |
958 | 243 | is changed.""" | 250 | is changed. |
959 | 251 | |||
960 | 252 | Note(coreycb): The method name with the _z_ is a little odd | ||
961 | 253 | but it forces the test to run last. It just makes things | ||
962 | 254 | easier because restarting services requires re-authorization. | ||
963 | 255 | """ | ||
964 | 244 | if self._get_openstack_release() >= self.precise_havana: | 256 | if self._get_openstack_release() >= self.precise_havana: |
965 | 245 | conf = '/etc/neutron/neutron.conf' | 257 | conf = '/etc/neutron/neutron.conf' |
966 | 246 | services = ['neutron-dhcp-agent', 'neutron-openvswitch-agent', | 258 | services = ['neutron-dhcp-agent', 'neutron-openvswitch-agent', |
967 | @@ -261,6 +273,7 @@ | |||
968 | 261 | for s in services: | 273 | for s in services: |
969 | 262 | if not u.service_restarted(self.quantum_gateway_sentry, s, conf, | 274 | if not u.service_restarted(self.quantum_gateway_sentry, s, conf, |
970 | 263 | pgrep_full=True, sleep_time=time): | 275 | pgrep_full=True, sleep_time=time): |
971 | 276 | self.d.configure('quantum-gateway', {'debug': 'False'}) | ||
972 | 264 | msg = "service {} didn't restart after config change".format(s) | 277 | msg = "service {} didn't restart after config change".format(s) |
973 | 265 | amulet.raise_status(amulet.FAIL, msg=msg) | 278 | amulet.raise_status(amulet.FAIL, msg=msg) |
974 | 266 | time = 0 | 279 | time = 0 |
975 | @@ -347,7 +360,7 @@ | |||
976 | 347 | 'ml2': { | 360 | 'ml2': { |
977 | 348 | 'type_drivers': 'gre,vxlan', | 361 | 'type_drivers': 'gre,vxlan', |
978 | 349 | 'tenant_network_types': 'gre,vxlan', | 362 | 'tenant_network_types': 'gre,vxlan', |
980 | 350 | 'mechanism_drivers': 'openvswitch' | 363 | 'mechanism_drivers': 'openvswitch,l2population' |
981 | 351 | }, | 364 | }, |
982 | 352 | 'ml2_type_gre': { | 365 | 'ml2_type_gre': { |
983 | 353 | 'tunnel_id_ranges': '1:1000' | 366 | 'tunnel_id_ranges': '1:1000' |
984 | @@ -629,7 +642,7 @@ | |||
985 | 629 | 'nova_metadata_port': '8775' | 642 | 'nova_metadata_port': '8775' |
986 | 630 | } | 643 | } |
987 | 631 | if self._get_openstack_release() >= self.precise_icehouse: | 644 | if self._get_openstack_release() >= self.precise_icehouse: |
989 | 632 | expected['cache_url'] = 'memory://?default_ttl=5' | 645 | expected['cache_url'] = 'memory://?default_ttl=5' |
990 | 633 | 646 | ||
991 | 634 | ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) | 647 | ret = u.validate_config_data(unit, conf, 'DEFAULT', expected) |
992 | 635 | if ret: | 648 | if ret: |
993 | 636 | 649 | ||
994 | === modified file 'tests/charmhelpers/contrib/amulet/deployment.py' | |||
995 | --- tests/charmhelpers/contrib/amulet/deployment.py 2014-09-25 15:37:05 +0000 | |||
996 | +++ tests/charmhelpers/contrib/amulet/deployment.py 2014-10-07 21:21:43 +0000 | |||
997 | @@ -25,25 +25,30 @@ | |||
998 | 25 | 25 | ||
999 | 26 | Add services to the deployment where this_service is the local charm | 26 | Add services to the deployment where this_service is the local charm |
1000 | 27 | that we're testing and other_services are the other services that | 27 | that we're testing and other_services are the other services that |
1002 | 28 | are being used in the amulet tests. | 28 | are being used in the local amulet tests. |
1003 | 29 | """ | 29 | """ |
1008 | 30 | name, units, location = range(3) | 30 | if this_service['name'] != os.path.basename(os.getcwd()): |
1009 | 31 | 31 | s = this_service['name'] | |
1006 | 32 | if this_service[name] != os.path.basename(os.getcwd()): | ||
1007 | 33 | s = this_service[name] | ||
1010 | 34 | msg = "The charm's root directory name needs to be {}".format(s) | 32 | msg = "The charm's root directory name needs to be {}".format(s) |
1011 | 35 | amulet.raise_status(amulet.FAIL, msg=msg) | 33 | amulet.raise_status(amulet.FAIL, msg=msg) |
1012 | 36 | 34 | ||
1014 | 37 | self.d.add(this_service[name], units=this_service[units]) | 35 | if 'units' not in this_service: |
1015 | 36 | this_service['units'] = 1 | ||
1016 | 37 | |||
1017 | 38 | self.d.add(this_service['name'], units=this_service['units']) | ||
1018 | 38 | 39 | ||
1019 | 39 | for svc in other_services: | 40 | for svc in other_services: |
1022 | 40 | if len(svc) > 2: | 41 | if 'location' in svc: |
1023 | 41 | branch_location = svc[location] | 42 | branch_location = svc['location'] |
1024 | 42 | elif self.series: | 43 | elif self.series: |
1026 | 43 | branch_location = 'cs:{}/{}'.format(self.series, svc[name]), | 44 | branch_location = 'cs:{}/{}'.format(self.series, svc['name']), |
1027 | 44 | else: | 45 | else: |
1028 | 45 | branch_location = None | 46 | branch_location = None |
1030 | 46 | self.d.add(svc[name], charm=branch_location, units=svc[units]) | 47 | |
1031 | 48 | if 'units' not in svc: | ||
1032 | 49 | svc['units'] = 1 | ||
1033 | 50 | |||
1034 | 51 | self.d.add(svc['name'], charm=branch_location, units=svc['units']) | ||
1035 | 47 | 52 | ||
1036 | 48 | def _add_relations(self, relations): | 53 | def _add_relations(self, relations): |
1037 | 49 | """Add all of the relations for the services.""" | 54 | """Add all of the relations for the services.""" |
1038 | 50 | 55 | ||
1039 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py' | |||
1040 | --- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-09-25 15:37:05 +0000 | |||
1041 | +++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-10-07 21:21:43 +0000 | |||
1042 | @@ -1,6 +1,3 @@ | |||
1043 | 1 | from bzrlib.branch import Branch | ||
1044 | 2 | import os | ||
1045 | 3 | import re | ||
1046 | 4 | from charmhelpers.contrib.amulet.deployment import ( | 1 | from charmhelpers.contrib.amulet.deployment import ( |
1047 | 5 | AmuletDeployment | 2 | AmuletDeployment |
1048 | 6 | ) | 3 | ) |
1049 | @@ -13,62 +10,62 @@ | |||
1050 | 13 | that is specifically for use by OpenStack charms. | 10 | that is specifically for use by OpenStack charms. |
1051 | 14 | """ | 11 | """ |
1052 | 15 | 12 | ||
1054 | 16 | def __init__(self, series=None, openstack=None, source=None): | 13 | def __init__(self, series=None, openstack=None, source=None, stable=True): |
1055 | 17 | """Initialize the deployment environment.""" | 14 | """Initialize the deployment environment.""" |
1056 | 18 | super(OpenStackAmuletDeployment, self).__init__(series) | 15 | super(OpenStackAmuletDeployment, self).__init__(series) |
1057 | 19 | self.openstack = openstack | 16 | self.openstack = openstack |
1058 | 20 | self.source = source | 17 | self.source = source |
1069 | 21 | 18 | self.stable = stable | |
1070 | 22 | def _is_dev_branch(self): | 19 | # Note(coreycb): this needs to be changed when new next branches come |
1071 | 23 | """Determine if branch being tested is a dev (i.e. next) branch.""" | 20 | # out. |
1072 | 24 | branch = Branch.open(os.getcwd()) | 21 | self.current_next = "trusty" |
1063 | 25 | parent = branch.get_parent() | ||
1064 | 26 | pattern = re.compile("^.*/next/$") | ||
1065 | 27 | if (pattern.match(parent)): | ||
1066 | 28 | return True | ||
1067 | 29 | else: | ||
1068 | 30 | return False | ||
1073 | 31 | 22 | ||
1074 | 32 | def _determine_branch_locations(self, other_services): | 23 | def _determine_branch_locations(self, other_services): |
1075 | 33 | """Determine the branch locations for the other services. | 24 | """Determine the branch locations for the other services. |
1076 | 34 | 25 | ||
1086 | 35 | If the branch being tested is a dev branch, then determine the | 26 | Determine if the local branch being tested is derived from its |
1087 | 36 | development branch locations for the other services. Otherwise, | 27 | stable or next (dev) branch, and based on this, use the corresonding |
1088 | 37 | the default charm store branches will be used.""" | 28 | stable or next branches for the other_services.""" |
1089 | 38 | name = 0 | 29 | base_charms = ['mysql', 'mongodb', 'rabbitmq-server'] |
1090 | 39 | if self._is_dev_branch(): | 30 | |
1091 | 40 | updated_services = [] | 31 | if self.stable: |
1092 | 41 | for svc in other_services: | 32 | for svc in other_services: |
1093 | 42 | if svc[name] in ['mysql', 'mongodb', 'rabbitmq-server']: | 33 | temp = 'lp:charms/{}' |
1094 | 43 | location = 'lp:charms/{}'.format(svc[name]) | 34 | svc['location'] = temp.format(svc['name']) |
1095 | 35 | else: | ||
1096 | 36 | for svc in other_services: | ||
1097 | 37 | if svc['name'] in base_charms: | ||
1098 | 38 | temp = 'lp:charms/{}' | ||
1099 | 39 | svc['location'] = temp.format(svc['name']) | ||
1100 | 44 | else: | 40 | else: |
1105 | 45 | temp = 'lp:~openstack-charmers/charms/trusty/{}/next' | 41 | temp = 'lp:~openstack-charmers/charms/{}/{}/next' |
1106 | 46 | location = temp.format(svc[name]) | 42 | svc['location'] = temp.format(self.current_next, |
1107 | 47 | updated_services.append(svc + (location,)) | 43 | svc['name']) |
1104 | 48 | other_services = updated_services | ||
1108 | 49 | return other_services | 44 | return other_services |
1109 | 50 | 45 | ||
1110 | 51 | def _add_services(self, this_service, other_services): | 46 | def _add_services(self, this_service, other_services): |
1111 | 52 | """Add services to the deployment and set openstack-origin/source.""" | 47 | """Add services to the deployment and set openstack-origin/source.""" |
1112 | 53 | name = 0 | ||
1113 | 54 | other_services = self._determine_branch_locations(other_services) | 48 | other_services = self._determine_branch_locations(other_services) |
1114 | 49 | |||
1115 | 55 | super(OpenStackAmuletDeployment, self)._add_services(this_service, | 50 | super(OpenStackAmuletDeployment, self)._add_services(this_service, |
1116 | 56 | other_services) | 51 | other_services) |
1117 | 52 | |||
1118 | 57 | services = other_services | 53 | services = other_services |
1119 | 58 | services.append(this_service) | 54 | services.append(this_service) |
1121 | 59 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph'] | 55 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
1122 | 56 | 'ceph-osd', 'ceph-radosgw'] | ||
1123 | 60 | 57 | ||
1124 | 61 | if self.openstack: | 58 | if self.openstack: |
1125 | 62 | for svc in services: | 59 | for svc in services: |
1127 | 63 | if svc[name] not in use_source: | 60 | if svc['name'] not in use_source: |
1128 | 64 | config = {'openstack-origin': self.openstack} | 61 | config = {'openstack-origin': self.openstack} |
1130 | 65 | self.d.configure(svc[name], config) | 62 | self.d.configure(svc['name'], config) |
1131 | 66 | 63 | ||
1132 | 67 | if self.source: | 64 | if self.source: |
1133 | 68 | for svc in services: | 65 | for svc in services: |
1135 | 69 | if svc[name] in use_source: | 66 | if svc['name'] in use_source: |
1136 | 70 | config = {'source': self.source} | 67 | config = {'source': self.source} |
1138 | 71 | self.d.configure(svc[name], config) | 68 | self.d.configure(svc['name'], config) |
1139 | 72 | 69 | ||
1140 | 73 | def _configure_services(self, configs): | 70 | def _configure_services(self, configs): |
1141 | 74 | """Configure all of the services.""" | 71 | """Configure all of the services.""" |