Merge lp:~jason-hobbs/juju-deployer/juju-deployer-populate-first into lp:juju-deployer
- juju-deployer-populate-first
- Merge into trunk
Status: | Rejected |
---|---|
Rejected by: | Haw Loeung |
Proposed branch: | lp:~jason-hobbs/juju-deployer/juju-deployer-populate-first |
Merge into: | lp:juju-deployer |
Diff against target: |
545 lines (+255/-41) 12 files modified
Makefile (+5/-1) deployer/action/importer.py (+137/-15) deployer/cli.py (+7/-0) deployer/deployment.py (+40/-8) deployer/env/go.py (+27/-0) deployer/env/py.py (+6/-0) deployer/service.py (+11/-4) deployer/tests/test_charm.py (+8/-0) deployer/tests/test_deployment.py (+7/-9) deployer/tests/test_diff.py (+3/-2) deployer/tests/test_guiserver.py (+3/-2) deployer/tests/test_importer.py (+1/-0) |
To merge this branch: | bzr merge lp:~jason-hobbs/juju-deployer/juju-deployer-populate-first |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
juju-deployers | Pending | ||
Review via email:
|
Commit message
Description of the change
Update of lp:~raharper/juju-deployer/populate-first - this merges a newer trunk. It also rename's rharper's add_machine to 'add_specific_
- 152. By Jason Hobbs
-
Fix syntax error and unit test.
- 153. By Jason Hobbs
-
Pull in lutostag's test fixes.
- 154. By Jason Hobbs
-
Force service sorting, and log it.
- 155. By Jason Hobbs
-
Specify series when adding a specific machine.
- 156. By Jason Hobbs
-
Use charm series if one provided by the charm.
This fixes deploying mixed Ubuntu/Windows bundles.
- 157. By Jason Hobbs
-
Timeout when acquiring a node takes too long.
Unmerged revisions
- 157. By Jason Hobbs
-
Timeout when acquiring a node takes too long.
- 156. By Jason Hobbs
-
Use charm series if one provided by the charm.
This fixes deploying mixed Ubuntu/Windows bundles.
- 155. By Jason Hobbs
-
Specify series when adding a specific machine.
- 154. By Jason Hobbs
-
Force service sorting, and log it.
- 153. By Jason Hobbs
-
Pull in lutostag's test fixes.
- 152. By Jason Hobbs
-
Fix syntax error and unit test.
- 151. By Jason Hobbs
-
Merge trunk.
- 150. By Ryan Harper
-
Allow nested placement when target uses maas= placement. Fix up debugging log message during deploy_services.
- 149. By Ryan Harper
-
Modify depoyment.
deploy_ services to bring services with placement and multiple units online to ensure subsequent services which placement can target previously deployed service units. Update get_machine to handle container placement. - 148. By Ryan Harper
-
Fix typo
Preview Diff
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2014-08-26 22:34:07 +0000 | |||
3 | +++ Makefile 2015-07-06 20:11:20 +0000 | |||
4 | @@ -1,5 +1,9 @@ | |||
5 | 1 | ifeq (,$(wildcard $(HOME)/.bazaar/bazaar.conf)) | ||
6 | 2 | PREFIX=HOME=/tmp | ||
7 | 3 | endif | ||
8 | 1 | test: | 4 | test: |
10 | 2 | nosetests -s --verbosity=2 deployer/tests | 5 | /bin/bash -c 'if [ -n "$(PREFIX)" ]; then mkdir -p /tmp/.juju; else true; fi' |
11 | 6 | /bin/bash -c "$(PREFIX) nosetests -s --verbosity=2 deployer/tests" | ||
12 | 3 | 7 | ||
13 | 4 | freeze: | 8 | freeze: |
14 | 5 | pip install -d tools/dist -r requirements.txt | 9 | pip install -d tools/dist -r requirements.txt |
15 | 6 | 10 | ||
16 | === modified file 'deployer/action/importer.py' | |||
17 | --- deployer/action/importer.py 2015-05-28 13:04:30 +0000 | |||
18 | +++ deployer/action/importer.py 2015-07-06 20:11:20 +0000 | |||
19 | @@ -1,5 +1,6 @@ | |||
20 | 1 | import logging | 1 | import logging |
21 | 2 | import time | 2 | import time |
22 | 3 | import math | ||
23 | 3 | 4 | ||
24 | 4 | from .base import BaseAction | 5 | from .base import BaseAction |
25 | 5 | from ..env import watchers | 6 | from ..env import watchers |
26 | @@ -58,7 +59,8 @@ | |||
27 | 58 | "Adding %d more units to %s" % (abs(delta), svc.name)) | 59 | "Adding %d more units to %s" % (abs(delta), svc.name)) |
28 | 59 | if svc.unit_placement: | 60 | if svc.unit_placement: |
29 | 60 | # Reload status once after non placed services units are done. | 61 | # Reload status once after non placed services units are done. |
31 | 61 | if reloaded is False: | 62 | |
32 | 63 | if self.options.placement_first is True or reloaded is False: | ||
33 | 62 | # Improved crappy workaround juju-core api inconsistency | 64 | # Improved crappy workaround juju-core api inconsistency |
34 | 63 | delay = time.time() + 60 | 65 | delay = time.time() + 60 |
35 | 64 | while delay > time.time(): | 66 | while delay > time.time(): |
36 | @@ -71,7 +73,8 @@ | |||
37 | 71 | 73 | ||
38 | 72 | placement = self.deployment.get_unit_placement(svc, env_status) | 74 | placement = self.deployment.get_unit_placement(svc, env_status) |
39 | 73 | for mid in range(cur_units, svc.num_units): | 75 | for mid in range(cur_units, svc.num_units): |
41 | 74 | self.env.add_unit(svc.name, placement.get(mid)) | 76 | self.env.add_unit(svc.name, |
42 | 77 | self.get_machine(placement.get(mid))) | ||
43 | 75 | else: | 78 | else: |
44 | 76 | self.env.add_units(svc.name, abs(delta)) | 79 | self.env.add_units(svc.name, abs(delta)) |
45 | 77 | 80 | ||
46 | @@ -181,13 +184,17 @@ | |||
47 | 181 | charm = self.deployment.get_charm_for(svc.name) | 184 | charm = self.deployment.get_charm_for(svc.name) |
48 | 182 | self.log.info( | 185 | self.log.info( |
49 | 183 | " Deploying service %s using %s", svc.name, charm.charm_url) | 186 | " Deploying service %s using %s", svc.name, charm.charm_url) |
50 | 187 | |||
51 | 188 | # Use charm series instead of deployment one if charm provides it. | ||
52 | 189 | # If charm does not provide series, this will be set to None. | ||
53 | 190 | charm_series = self.deployment.get_charm_series(svc.name) | ||
54 | 184 | 191 | ||
55 | 185 | if svc.unit_placement: | 192 | if svc.unit_placement: |
56 | 186 | # We sorted all the non placed services first, so we only | 193 | # We sorted all the non placed services first, so we only |
57 | 187 | # need to update status once after we're done with them, in | 194 | # need to update status once after we're done with them, in |
58 | 188 | # the instance of v3 bundles; in the more complex case of v4 | 195 | # the instance of v3 bundles; in the more complex case of v4 |
59 | 189 | # bundles, we'll need to refresh each time. | 196 | # bundles, we'll need to refresh each time. |
61 | 190 | if not reloaded: | 197 | if self.options.placement_first or not reloaded: |
62 | 191 | self.log.debug( | 198 | self.log.debug( |
63 | 192 | " Refetching status for placement deploys") | 199 | " Refetching status for placement deploys") |
64 | 193 | time.sleep(5.1) | 200 | time.sleep(5.1) |
65 | @@ -210,18 +217,59 @@ | |||
66 | 210 | num_units = None | 217 | num_units = None |
67 | 211 | 218 | ||
68 | 212 | placement = self.deployment.get_unit_placement(svc, env_status) | 219 | placement = self.deployment.get_unit_placement(svc, env_status) |
81 | 213 | 220 | # allocate all of the machines up front for all units | |
82 | 214 | if charm.is_subordinate(): | 221 | # to ensure we don't allocate a targeted machine to |
83 | 215 | num_units = None | 222 | # a service without placement |
84 | 216 | 223 | if svc.unit_placement and \ | |
85 | 217 | self.env.deploy( | 224 | svc.num_units > 1 and \ |
86 | 218 | svc.name, | 225 | self.options.placement_first is True: |
87 | 219 | charm.charm_url, | 226 | self.log.debug('Pre-allocating machines for %s' % svc.name) |
88 | 220 | self.deployment.repo_path, | 227 | self.log.debug('Deploy base service: %s' % svc.name) |
89 | 221 | svc.config, | 228 | p = placement.get(0) |
90 | 222 | svc.constraints, | 229 | machine = self.get_machine(p, charm_series) |
91 | 223 | num_units, | 230 | self.log.debug('deploy_services: ' |
92 | 224 | placement.get(0)) | 231 | 'service=%s unit=0 placement=%s machine=%s' % |
93 | 232 | (svc.name, p, machine)) | ||
94 | 233 | num_units = 1 | ||
95 | 234 | # deploy base service | ||
96 | 235 | self.env.deploy( | ||
97 | 236 | svc.name, | ||
98 | 237 | charm.charm_url, | ||
99 | 238 | self.deployment.repo_path, | ||
100 | 239 | svc.config, | ||
101 | 240 | svc.constraints, | ||
102 | 241 | num_units, | ||
103 | 242 | machine) | ||
104 | 243 | |||
105 | 244 | # add additional units | ||
106 | 245 | time.sleep(5.1) | ||
107 | 246 | env_status = self.env.status() | ||
108 | 247 | cur_units = len(env_status['services'][svc.name].get('units', ())) | ||
109 | 248 | placement = self.deployment.get_unit_placement(svc, env_status) | ||
110 | 249 | for uid in range(cur_units, svc.num_units): | ||
111 | 250 | p = placement.get(uid) | ||
112 | 251 | machine = self.get_machine(p, charm_series) | ||
113 | 252 | self.log.debug('add_units: ' | ||
114 | 253 | 'service=%s unit=%s placement=%s machine=%s' % | ||
115 | 254 | (svc.name, uid, p, machine)) | ||
116 | 255 | self.env.add_unit(svc.name, machine) | ||
117 | 256 | |||
118 | 257 | |||
119 | 258 | else: | ||
120 | 259 | # just let add_units handling bring additional units on-line | ||
121 | 260 | num_units = 1 | ||
122 | 261 | |||
123 | 262 | if charm.is_subordinate(): | ||
124 | 263 | num_units = None | ||
125 | 264 | |||
126 | 265 | self.env.deploy( | ||
127 | 266 | svc.name, | ||
128 | 267 | charm.charm_url, | ||
129 | 268 | self.deployment.repo_path, | ||
130 | 269 | svc.config, | ||
131 | 270 | svc.constraints, | ||
132 | 271 | num_units, | ||
133 | 272 | self.get_machine(placement.get(0), charm_series)) | ||
134 | 225 | 273 | ||
135 | 226 | if svc.annotations: | 274 | if svc.annotations: |
136 | 227 | self.log.debug(" Setting annotations") | 275 | self.log.debug(" Setting annotations") |
137 | @@ -294,6 +342,80 @@ | |||
138 | 294 | int(timeout), watch=self.options.watch, | 342 | int(timeout), watch=self.options.watch, |
139 | 295 | services=self.deployment.get_service_names(), on_errors=on_errors) | 343 | services=self.deployment.get_service_names(), on_errors=on_errors) |
140 | 296 | 344 | ||
141 | 345 | def get_machine(self, u_idx, charm_series=None): | ||
142 | 346 | # find the machine id that matches the target machine | ||
143 | 347 | # unlike juju status output, the dns-name is one of the | ||
144 | 348 | # many values returned from our env.status() in addresses | ||
145 | 349 | if u_idx is None: | ||
146 | 350 | return None | ||
147 | 351 | |||
148 | 352 | status = self.env.status() | ||
149 | 353 | # lxc:1 kvm:1, or 1 | ||
150 | 354 | if ':' in u_idx or u_idx.isdigit(): | ||
151 | 355 | mid = [u_idx] | ||
152 | 356 | else: | ||
153 | 357 | mid = [x for x in status['machines'].keys() | ||
154 | 358 | if u_idx in | ||
155 | 359 | [v.get('Value') for v in | ||
156 | 360 | status['machines'][x]['addresses']]] | ||
157 | 361 | self.deployment.log.info('mid=%s' % mid) | ||
158 | 362 | if mid: | ||
159 | 363 | m = mid.pop() | ||
160 | 364 | self.deployment.log.debug( | ||
161 | 365 | 'Found juju machine (%s) matching placement: %s', m, u_idx) | ||
162 | 366 | return m | ||
163 | 367 | else: | ||
164 | 368 | self.deployment.log.info( | ||
165 | 369 | 'No match in juju machines for: %s', u_idx) | ||
166 | 370 | |||
167 | 371 | # if we don't find a match, we need to add it | ||
168 | 372 | series = self.deployment.data['series'] | ||
169 | 373 | if (charm_series is not None) and (charm_series != series): | ||
170 | 374 | # override series if different from deployment series | ||
171 | 375 | series = charm_series | ||
172 | 376 | |||
173 | 377 | mid = self.env.add_specific_machine(u_idx, series) | ||
174 | 378 | |||
175 | 379 | # timeout set to ~17 minutes which is much larger than needed | ||
176 | 380 | backoff = 2 | ||
177 | 381 | delay = 1 | ||
178 | 382 | timeout = math.pow(2, 10) | ||
179 | 383 | |||
180 | 384 | self.deployment.log.debug( | ||
181 | 385 | 'Waiting for machine to show up in status.') | ||
182 | 386 | while True: | ||
183 | 387 | m = mid.get('Machine') | ||
184 | 388 | if m in status['machines'].keys(): | ||
185 | 389 | s = [x for x in status['machines'].keys() | ||
186 | 390 | if u_idx in | ||
187 | 391 | [v.get('Value') for v in | ||
188 | 392 | status['machines'][x]['addresses']]] | ||
189 | 393 | self.deployment.log.debug('addresses: %s' % s) | ||
190 | 394 | if m in s: | ||
191 | 395 | break | ||
192 | 396 | else: | ||
193 | 397 | self.deployment.log.debug( | ||
194 | 398 | 'Machine %s not in status yet' % m) | ||
195 | 399 | |||
196 | 400 | self.deployment.log.debug("Sleep for %s second(s).", | ||
197 | 401 | str(delay)) | ||
198 | 402 | time.sleep(delay) | ||
199 | 403 | delay *= backoff | ||
200 | 404 | |||
201 | 405 | # The case of a machine failing to show up is really a | ||
202 | 406 | # an unrecoverable failure that can't be dealt with as upstream | ||
203 | 407 | # is not handling this case. So, the deployment must fail here. | ||
204 | 408 | if timeout-delay <= 0: | ||
205 | 409 | self.deployment.log.error( | ||
206 | 410 | "Deployment has failed. Machine %s did not show up.", | ||
207 | 411 | u_idx) | ||
208 | 412 | raise ErrorExit() | ||
209 | 413 | |||
210 | 414 | |||
211 | 415 | status = self.env.status() | ||
212 | 416 | self.deployment.log.debug('Machine %s up!' % m) | ||
213 | 417 | return mid.get('Machine') | ||
214 | 418 | |||
215 | 297 | def run(self): | 419 | def run(self): |
216 | 298 | options = self.options | 420 | options = self.options |
217 | 299 | self.start_time = time.time() | 421 | self.start_time = time.time() |
218 | 300 | 422 | ||
219 | === modified file 'deployer/cli.py' | |||
220 | --- deployer/cli.py 2014-10-01 10:18:36 +0000 | |||
221 | +++ deployer/cli.py 2015-07-06 20:11:20 +0000 | |||
222 | @@ -82,6 +82,13 @@ | |||
223 | 82 | "machine removal."), | 82 | "machine removal."), |
224 | 83 | dest="deploy_delay", default=0) | 83 | dest="deploy_delay", default=0) |
225 | 84 | parser.add_argument( | 84 | parser.add_argument( |
226 | 85 | '-P', '--placement-first', action='store_true', default=False, | ||
227 | 86 | dest='placement_first', | ||
228 | 87 | help=("Sort services with placement services first to " | ||
229 | 88 | "ensure that the requirement machines are aquired " | ||
230 | 89 | "before non-targeted services are deployed. Note " | ||
231 | 90 | "this reverses the default sorting order.")) | ||
232 | 91 | parser.add_argument( | ||
233 | 85 | '-e', '--environment', action='store', dest='juju_env', | 92 | '-e', '--environment', action='store', dest='juju_env', |
234 | 86 | help='Deploy to a specific Juju environment.', | 93 | help='Deploy to a specific Juju environment.', |
235 | 87 | default=os.getenv('JUJU_ENV')) | 94 | default=os.getenv('JUJU_ENV')) |
236 | 88 | 95 | ||
237 | === modified file 'deployer/deployment.py' | |||
238 | --- deployer/deployment.py 2015-05-13 14:16:46 +0000 | |||
239 | +++ deployer/deployment.py 2015-07-06 20:11:20 +0000 | |||
240 | @@ -46,11 +46,10 @@ | |||
241 | 46 | services = [] | 46 | services = [] |
242 | 47 | for name, svc_data in self.data.get('services', {}).items(): | 47 | for name, svc_data in self.data.get('services', {}).items(): |
243 | 48 | services.append(Service(name, svc_data)) | 48 | services.append(Service(name, svc_data)) |
249 | 49 | if self.version == 3: | 49 | |
250 | 50 | # Sort unplaced units first, then sort by name for placed units. | 50 | self.log.debug("Sorting services: %s" % services) |
251 | 51 | services.sort(key=lambda svc: (bool(svc.unit_placement), svc.name)) | 51 | services.sort(self._services_sort) |
252 | 52 | else: | 52 | self.log.debug("Sorted services: %s" % services) |
248 | 53 | services.sort(self._machines_placement_sort) | ||
253 | 54 | return services | 53 | return services |
254 | 55 | 54 | ||
255 | 56 | def set_machines(self, machines): | 55 | def set_machines(self, machines): |
256 | @@ -81,7 +80,7 @@ | |||
257 | 81 | return self.data.get('services', {}).keys() | 80 | return self.data.get('services', {}).keys() |
258 | 82 | 81 | ||
259 | 83 | @staticmethod | 82 | @staticmethod |
261 | 84 | def _machines_placement_sort(svc_a, svc_b): | 83 | def _services_sort(svc_a, svc_b): |
262 | 85 | """Sort machines with machine placement in mind. | 84 | """Sort machines with machine placement in mind. |
263 | 86 | 85 | ||
264 | 87 | If svc_a is colocated alongside svc_b, svc_b needs to be deployed | 86 | If svc_a is colocated alongside svc_b, svc_b needs to be deployed |
265 | @@ -90,6 +89,35 @@ | |||
266 | 90 | whether or not the service has a unit placement, and then finally | 89 | whether or not the service has a unit placement, and then finally |
267 | 91 | based on the name of the service. | 90 | based on the name of the service. |
268 | 92 | """ | 91 | """ |
269 | 92 | def _placement_sort(svc_a, svc_b): | ||
270 | 93 | """ Sorts unit_placement lists, | ||
271 | 94 | putting maas= units at the front""" | ||
272 | 95 | def maas_first(a, b): | ||
273 | 96 | if a.startswith('maas='): | ||
274 | 97 | if b.startswith('maas='): | ||
275 | 98 | return cmp(a, b) | ||
276 | 99 | return -1 | ||
277 | 100 | if b.startswith('maas='): | ||
278 | 101 | return 1 | ||
279 | 102 | |||
280 | 103 | if ':' in a: | ||
281 | 104 | if ':' in b: | ||
282 | 105 | return cmp(a, b) | ||
283 | 106 | return 1 | ||
284 | 107 | |||
285 | 108 | return cmp(a, b) | ||
286 | 109 | |||
287 | 110 | # sort both services' unit_placement lists | ||
288 | 111 | # putting maas units first | ||
289 | 112 | svc_a.unit_placement.sort(cmp=maas_first) | ||
290 | 113 | svc_b.unit_placement.sort(cmp=maas_first) | ||
291 | 114 | |||
292 | 115 | # now compare the service placement lists, | ||
293 | 116 | # first list with a maas placement goes first | ||
294 | 117 | for x, y in zip(svc_a.unit_placement, | ||
295 | 118 | svc_b.unit_placement): | ||
296 | 119 | return maas_first(x, y) | ||
297 | 120 | |||
298 | 93 | if svc_a.unit_placement: | 121 | if svc_a.unit_placement: |
299 | 94 | if svc_b.unit_placement: | 122 | if svc_b.unit_placement: |
300 | 95 | # Check for colocation. This naively assumes that there is no | 123 | # Check for colocation. This naively assumes that there is no |
301 | @@ -98,8 +126,8 @@ | |||
302 | 98 | return -1 | 126 | return -1 |
303 | 99 | if x_in_y(svc_a, svc_b): | 127 | if x_in_y(svc_a, svc_b): |
304 | 100 | return 1 | 128 | return 1 |
307 | 101 | # If no colocation exists, simply compare names. | 129 | # If no colocation exists, do a placement sort. |
308 | 102 | return cmp(svc_a.name, svc_b.name) | 130 | return _placement_sort(svc_a, svc_b) |
309 | 103 | return 1 | 131 | return 1 |
310 | 104 | if svc_b.unit_placement: | 132 | if svc_b.unit_placement: |
311 | 105 | return -1 | 133 | return -1 |
312 | @@ -166,6 +194,10 @@ | |||
313 | 166 | return Charm.from_service( | 194 | return Charm.from_service( |
314 | 167 | svc_name, self.repo_path, self.series, svc_data) | 195 | svc_name, self.repo_path, self.series, svc_data) |
315 | 168 | 196 | ||
316 | 197 | def get_charm_series(self, svc_name): | ||
317 | 198 | svc_data = self.data['services'][svc_name] | ||
318 | 199 | return svc_data.get('series') | ||
319 | 200 | |||
320 | 169 | def fetch_charms(self, update=False, no_local_mods=False): | 201 | def fetch_charms(self, update=False, no_local_mods=False): |
321 | 170 | for charm in self.get_charms(): | 202 | for charm in self.get_charms(): |
322 | 171 | if charm.is_local(): | 203 | if charm.is_local(): |
323 | 172 | 204 | ||
324 | === modified file 'deployer/env/go.py' | |||
325 | --- deployer/env/go.py 2015-03-12 18:41:43 +0000 | |||
326 | +++ deployer/env/go.py 2015-07-06 20:11:20 +0000 | |||
327 | @@ -41,6 +41,30 @@ | |||
328 | 41 | series=series, | 41 | series=series, |
329 | 42 | constraints=parse_constraints(constraints))['Machine'] | 42 | constraints=parse_constraints(constraints))['Machine'] |
330 | 43 | 43 | ||
331 | 44 | def add_specific_machine(self, machine, series=""): | ||
332 | 45 | if ':' in machine: | ||
333 | 46 | scope, directive = machine.split(':') | ||
334 | 47 | else: | ||
335 | 48 | scope = self.get_env_config()['Config']['uuid'] | ||
336 | 49 | directive = machine | ||
337 | 50 | |||
338 | 51 | machines = [{ | ||
339 | 52 | "Placement": { | ||
340 | 53 | "Scope": scope, | ||
341 | 54 | "Directive": directive, | ||
342 | 55 | }, | ||
343 | 56 | "ParentId": "", | ||
344 | 57 | "ContainerType": "", | ||
345 | 58 | "Series": series, | ||
346 | 59 | "Constraints": {}, | ||
347 | 60 | "Jobs": [ | ||
348 | 61 | "JobHostUnits" | ||
349 | 62 | ] | ||
350 | 63 | }] | ||
351 | 64 | self.log.debug('Adding machine: %s:%s' % (scope, directive)) | ||
352 | 65 | # {u'Machines': [{u'Machine': u'7', u'Error': None}]} | ||
353 | 66 | return self.client.add_machines(machines)['Machines'][0] | ||
354 | 67 | |||
355 | 44 | def add_unit(self, service_name, machine_spec): | 68 | def add_unit(self, service_name, machine_spec): |
356 | 45 | return self.client.add_unit(service_name, machine_spec) | 69 | return self.client.add_unit(service_name, machine_spec) |
357 | 46 | 70 | ||
358 | @@ -68,6 +92,9 @@ | |||
359 | 68 | def get_config(self, svc_name): | 92 | def get_config(self, svc_name): |
360 | 69 | return self.client.get_config(svc_name) | 93 | return self.client.get_config(svc_name) |
361 | 70 | 94 | ||
362 | 95 | def get_env_config(self): | ||
363 | 96 | return self.client.get_env_config() | ||
364 | 97 | |||
365 | 71 | def get_constraints(self, svc_name): | 98 | def get_constraints(self, svc_name): |
366 | 72 | try: | 99 | try: |
367 | 73 | return self.client.get_constraints(svc_name) | 100 | return self.client.get_constraints(svc_name) |
368 | 74 | 101 | ||
369 | === modified file 'deployer/env/py.py' | |||
370 | --- deployer/env/py.py 2014-02-18 12:16:46 +0000 | |||
371 | +++ deployer/env/py.py 2015-07-06 20:11:20 +0000 | |||
372 | @@ -12,6 +12,12 @@ | |||
373 | 12 | self.name = name | 12 | self.name = name |
374 | 13 | self.options = options | 13 | self.options = options |
375 | 14 | 14 | ||
376 | 15 | def add_machine(self, machine): | ||
377 | 16 | params - self._named_env(["juju", "add-machine"]) | ||
378 | 17 | params.extend([machine]) | ||
379 | 18 | self._check_call( | ||
380 | 19 | params, self.log, "Error adding machine %s", machine) | ||
381 | 20 | |||
382 | 15 | def add_units(self, service_name, num_units): | 21 | def add_units(self, service_name, num_units): |
383 | 16 | params = self._named_env(["juju", "add-unit"]) | 22 | params = self._named_env(["juju", "add-unit"]) |
384 | 17 | if num_units > 1: | 23 | if num_units > 1: |
385 | 18 | 24 | ||
386 | === modified file 'deployer/service.py' | |||
387 | --- deployer/service.py 2015-03-18 18:19:43 +0000 | |||
388 | +++ deployer/service.py 2015-07-06 20:11:20 +0000 | |||
389 | @@ -152,12 +152,19 @@ | |||
390 | 152 | feedback.error( | 152 | feedback.error( |
391 | 153 | ("Service placement to machine" | 153 | ("Service placement to machine" |
392 | 154 | "not supported %s to %s") % ( | 154 | "not supported %s to %s") % ( |
394 | 155 | self.service.name, unit_placement[idx])) | 155 | self.service.name, unit_placement[idx])) |
395 | 156 | elif p in services: | 156 | elif p in services: |
396 | 157 | if services[p].unit_placement: | 157 | if services[p].unit_placement: |
400 | 158 | feedback.error( | 158 | # nested placement is acceptable if the target |
401 | 159 | "Nested placement not supported %s -> %s -> %s" % ( | 159 | # is using maas node placement |
402 | 160 | self.service.name, p, services[p].unit_placement)) | 160 | for u in services[p].unit_placement: |
403 | 161 | if not u.startswith('maas='): | ||
404 | 162 | feedback.error( | ||
405 | 163 | "Nested placement not supported" | ||
406 | 164 | " %s -> %s -> %s" % ( | ||
407 | 165 | self.service.name, p, | ||
408 | 166 | services[p].unit_placement)) | ||
409 | 167 | continue | ||
410 | 161 | elif self.deployment.get_charm_for(p).is_subordinate(): | 168 | elif self.deployment.get_charm_for(p).is_subordinate(): |
411 | 162 | feedback.error( | 169 | feedback.error( |
412 | 163 | "Cannot place to a subordinate service: %s -> %s" % ( | 170 | "Cannot place to a subordinate service: %s -> %s" % ( |
413 | 164 | 171 | ||
414 | === modified file 'deployer/tests/test_charm.py' | |||
415 | --- deployer/tests/test_charm.py 2014-09-29 14:36:34 +0000 | |||
416 | +++ deployer/tests/test_charm.py 2015-07-06 20:11:20 +0000 | |||
417 | @@ -161,6 +161,14 @@ | |||
418 | 161 | self._call( | 161 | self._call( |
419 | 162 | ["git", "init", self.path], | 162 | ["git", "init", self.path], |
420 | 163 | "Could not initialize repo at %(path)s") | 163 | "Could not initialize repo at %(path)s") |
421 | 164 | self._call( | ||
422 | 165 | ["git", "config", | ||
423 | 166 | "user.email", "test@example.com"], | ||
424 | 167 | "Could not config user.email at %(path)s") | ||
425 | 168 | self._call( | ||
426 | 169 | ["git", "config", | ||
427 | 170 | "user.name", "test"], | ||
428 | 171 | "Could not config user.name at %(path)s") | ||
429 | 164 | 172 | ||
430 | 165 | def write(self, files): | 173 | def write(self, files): |
431 | 166 | for f in files: | 174 | for f in files: |
432 | 167 | 175 | ||
433 | === modified file 'deployer/tests/test_deployment.py' | |||
434 | --- deployer/tests/test_deployment.py 2015-03-18 18:19:43 +0000 | |||
435 | +++ deployer/tests/test_deployment.py 2015-07-06 20:11:20 +0000 | |||
436 | @@ -75,8 +75,6 @@ | |||
437 | 75 | def test_maas_name_and_zone_placement(self): | 75 | def test_maas_name_and_zone_placement(self): |
438 | 76 | d = self.get_named_deployment_v3("stack-placement-maas.yml", "stack") | 76 | d = self.get_named_deployment_v3("stack-placement-maas.yml", "stack") |
439 | 77 | d.validate_placement() | 77 | d.validate_placement() |
440 | 78 | placement = d.get_unit_placement('ceph', {}) | ||
441 | 79 | self.assertEqual(placement.get(0), "arnolt") | ||
442 | 80 | placement = d.get_unit_placement('heat', {}) | 78 | placement = d.get_unit_placement('heat', {}) |
443 | 81 | self.assertEqual(placement.get(0), "zone=zebra") | 79 | self.assertEqual(placement.get(0), "zone=zebra") |
444 | 82 | 80 | ||
445 | @@ -90,35 +88,35 @@ | |||
446 | 90 | except ErrorExit: | 88 | except ErrorExit: |
447 | 91 | self.fail("Should not fail") | 89 | self.fail("Should not fail") |
448 | 92 | 90 | ||
450 | 93 | def test_machines_placement_sort(self): | 91 | def test_services_sort(self): |
451 | 94 | d = Deployment('test', None, None) | 92 | d = Deployment('test', None, None) |
452 | 95 | self.assertEqual( | 93 | self.assertEqual( |
454 | 96 | d._machines_placement_sort( | 94 | d._services_sort( |
455 | 97 | FauxService(unit_placement=1), | 95 | FauxService(unit_placement=1), |
456 | 98 | FauxService() | 96 | FauxService() |
457 | 99 | ), 1) | 97 | ), 1) |
458 | 100 | self.assertEqual( | 98 | self.assertEqual( |
460 | 101 | d._machines_placement_sort( | 99 | d._services_sort( |
461 | 102 | FauxService(), | 100 | FauxService(), |
462 | 103 | FauxService(unit_placement=1) | 101 | FauxService(unit_placement=1) |
463 | 104 | ), -1) | 102 | ), -1) |
464 | 105 | self.assertEqual( | 103 | self.assertEqual( |
466 | 106 | d._machines_placement_sort( | 104 | d._services_sort( |
467 | 107 | FauxService(name="x", unit_placement=['asdf']), | 105 | FauxService(name="x", unit_placement=['asdf']), |
468 | 108 | FauxService(name="y", unit_placement=['lxc:x/1']) | 106 | FauxService(name="y", unit_placement=['lxc:x/1']) |
469 | 109 | ), 1) | 107 | ), 1) |
470 | 110 | self.assertEqual( | 108 | self.assertEqual( |
472 | 111 | d._machines_placement_sort( | 109 | d._services_sort( |
473 | 112 | FauxService(name="y", unit_placement=['lxc:x/1']), | 110 | FauxService(name="y", unit_placement=['lxc:x/1']), |
474 | 113 | FauxService(name="x", unit_placement=['asdf']) | 111 | FauxService(name="x", unit_placement=['asdf']) |
475 | 114 | ), -1) | 112 | ), -1) |
476 | 115 | self.assertEqual( | 113 | self.assertEqual( |
478 | 116 | d._machines_placement_sort( | 114 | d._services_sort( |
479 | 117 | FauxService(name="x", unit_placement=['asdf']), | 115 | FauxService(name="x", unit_placement=['asdf']), |
480 | 118 | FauxService(name="y", unit_placement=['hjkl']) | 116 | FauxService(name="y", unit_placement=['hjkl']) |
481 | 119 | ), -1) | 117 | ), -1) |
482 | 120 | self.assertEqual( | 118 | self.assertEqual( |
484 | 121 | d._machines_placement_sort( | 119 | d._services_sort( |
485 | 122 | FauxService(name="x"), | 120 | FauxService(name="x"), |
486 | 123 | FauxService(name="y") | 121 | FauxService(name="y") |
487 | 124 | ), -1) | 122 | ), -1) |
488 | 125 | 123 | ||
489 | === modified file 'deployer/tests/test_diff.py' | |||
490 | --- deployer/tests/test_diff.py 2015-03-17 17:34:57 +0000 | |||
491 | +++ deployer/tests/test_diff.py 2015-07-06 20:11:20 +0000 | |||
492 | @@ -35,8 +35,9 @@ | |||
493 | 35 | cls._dir = tempfile.mkdtemp() | 35 | cls._dir = tempfile.mkdtemp() |
494 | 36 | os.mkdir(os.path.join(cls._dir, "precise")) | 36 | os.mkdir(os.path.join(cls._dir, "precise")) |
495 | 37 | deployment.repo_path = cls._dir | 37 | deployment.repo_path = cls._dir |
498 | 38 | deployment.fetch_charms() | 38 | if not TEST_OFFLINE: |
499 | 39 | deployment.resolve() | 39 | deployment.fetch_charms() |
500 | 40 | deployment.resolve() | ||
501 | 40 | cls._deployment = deployment | 41 | cls._deployment = deployment |
502 | 41 | 42 | ||
503 | 42 | @classmethod | 43 | @classmethod |
504 | 43 | 44 | ||
505 | === modified file 'deployer/tests/test_guiserver.py' | |||
506 | --- deployer/tests/test_guiserver.py 2015-03-17 10:40:34 +0000 | |||
507 | +++ deployer/tests/test_guiserver.py 2015-07-06 20:11:20 +0000 | |||
508 | @@ -28,7 +28,7 @@ | |||
509 | 28 | 'find_service', 'ignore_errors', 'juju_env', 'list_deploys', | 28 | 'find_service', 'ignore_errors', 'juju_env', 'list_deploys', |
510 | 29 | 'no_local_mods', 'no_relations', 'overrides', 'rel_wait', | 29 | 'no_local_mods', 'no_relations', 'overrides', 'rel_wait', |
511 | 30 | 'retry_count', 'series', 'skip_unit_wait', 'terminate_machines', | 30 | 'retry_count', 'series', 'skip_unit_wait', 'terminate_machines', |
513 | 31 | 'timeout', 'update_charms', 'verbose', 'watch' | 31 | 'timeout', 'update_charms', 'verbose', 'watch', 'placement_first', |
514 | 32 | ]) | 32 | ]) |
515 | 33 | self.assertEqual(expected_keys, set(self.options.__dict__.keys())) | 33 | self.assertEqual(expected_keys, set(self.options.__dict__.keys())) |
516 | 34 | 34 | ||
517 | @@ -58,6 +58,7 @@ | |||
518 | 58 | self.assertFalse(options.update_charms) | 58 | self.assertFalse(options.update_charms) |
519 | 59 | self.assertFalse(options.verbose) | 59 | self.assertFalse(options.verbose) |
520 | 60 | self.assertFalse(options.watch) | 60 | self.assertFalse(options.watch) |
521 | 61 | self.assertFalse(options.placement_first) | ||
522 | 61 | 62 | ||
523 | 62 | 63 | ||
524 | 63 | class TestDeploymentError(unittest.TestCase): | 64 | class TestDeploymentError(unittest.TestCase): |
525 | @@ -280,7 +281,7 @@ | |||
526 | 280 | mock.call.status(), | 281 | mock.call.status(), |
527 | 281 | mock.call.deploy( | 282 | mock.call.deploy( |
528 | 282 | 'mysql', 'cs:precise/mysql-28', '', None, | 283 | 'mysql', 'cs:precise/mysql-28', '', None, |
530 | 283 | {'arch': 'i386', 'cpu-cores': 4, 'mem': '4G'}, 2, None), | 284 | {'arch': 'i386', 'cpu-cores': 4, 'mem': '4G'}, 1, None), |
531 | 284 | mock.call.set_annotation( | 285 | mock.call.set_annotation( |
532 | 285 | 'mysql', {'gui-y': '164.547', 'gui-x': '494.347'}), | 286 | 'mysql', {'gui-y': '164.547', 'gui-x': '494.347'}), |
533 | 286 | mock.call.deploy( | 287 | mock.call.deploy( |
534 | 287 | 288 | ||
535 | === modified file 'deployer/tests/test_importer.py' | |||
536 | --- deployer/tests/test_importer.py 2015-05-13 14:06:34 +0000 | |||
537 | +++ deployer/tests/test_importer.py 2015-07-06 20:11:20 +0000 | |||
538 | @@ -34,6 +34,7 @@ | |||
539 | 34 | 'no_local_mods': True, | 34 | 'no_local_mods': True, |
540 | 35 | 'no_relations': False, | 35 | 'no_relations': False, |
541 | 36 | 'overrides': None, | 36 | 'overrides': None, |
542 | 37 | 'placement_first': False, | ||
543 | 37 | 'rel_wait': 60, | 38 | 'rel_wait': 60, |
544 | 38 | 'retry_count': 0, | 39 | 'retry_count': 0, |
545 | 39 | 'series': None, | 40 | 'series': None, |