Merge ~mthaddon/juju-upgrader/+git/juju-upgrader:juju-status into juju-upgrader:master
- Git
- lp:~mthaddon/juju-upgrader/+git/juju-upgrader
- juju-status
- Merge into master
Proposed by
Tom Haddon
Status: | Merged |
---|---|
Approved by: | Joel Sing |
Approved revision: | 8f977f385d1c13622ae9df1b3cc38696809b4652 |
Merged at revision: | bbd6639f059e1a189eaf436dc6fa2102828b2e28 |
Proposed branch: | ~mthaddon/juju-upgrader/+git/juju-upgrader:juju-status |
Merge into: | juju-upgrader:master |
Diff against target: |
896 lines (+651/-209) 4 files modified
juju_status.py (+142/-0) unit_tests/test_juju_statistics.py (+3/-209) unit_tests/test_juju_status.py (+288/-0) unit_tests/testdata/test_statuses.py (+218/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joel Sing (community) | +1 | Approve | |
Tom Haddon | Needs Resubmitting | ||
Review via email: mp+335714@code.launchpad.net |
Commit message
Add juju status and unit tests
Description of the change
Add juju_status and unit tests
To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Revision history for this message
Tom Haddon (mthaddon) wrote : | # |
This doesn't include unit tests for check_model function, which largely just calls other functions tested elsewhere.
Revision history for this message
Joel Sing (jsing) wrote : | # |
LGTM, but see comments.
review:
Approve
(+1)
Revision history for this message
Tom Haddon (mthaddon) wrote : | # |
One question inline about where to put the code (I agree directly in the home dir isn't the best).
Will push up other changes shortly.
Revision history for this message
Joel Sing (jsing) : | # |
Revision history for this message
Tom Haddon (mthaddon) : | # |
Revision history for this message
Tom Haddon (mthaddon) wrote : | # |
Have updated based on discussions about where we should copy code to on machines. PTAL.
review:
Needs Resubmitting
Revision history for this message
Joel Sing (jsing) wrote : | # |
Just a couple of minor things.
review:
Approve
(+1)
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Merge proposal is approved, but source repository revision has changed, setting status to needs review.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision bbd6639f059e1a1
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/juju_status.py b/juju_status.py |
2 | new file mode 100644 |
3 | index 0000000..d096ddd |
4 | --- /dev/null |
5 | +++ b/juju_status.py |
6 | @@ -0,0 +1,142 @@ |
7 | +# Copyright (c) 2017, 2018 Canonical Ltd |
8 | +# License: GPLv3 |
9 | +# Authors: Paul Gear <paul.gear@canonical.com> |
10 | +# Tom Haddon <tom.haddon@canonical.com> |
11 | + |
12 | +import glob |
13 | +import json |
14 | +import os.path |
15 | +import subprocess |
16 | +import sys |
17 | + |
18 | +import juju_utils |
19 | + |
20 | + |
21 | +def _get_model_details(status): |
22 | + m = status.get('model', {}) |
23 | + return { |
24 | + "model": m.get('name', None), |
25 | + "model-version": m.get('version', None), |
26 | + } |
27 | + |
28 | + |
29 | +def _get_juju_agent_status(obj): |
30 | + if 'juju-status' in obj: |
31 | + obj['juju-status']['agent-time'] = juju_utils.parse_time('since', obj['juju-status']) |
32 | + return obj['juju-status'] |
33 | + else: |
34 | + return {} |
35 | + |
36 | + |
37 | +def _get_unit(name, obj, status=None, machine=None, machine_version=None): |
38 | + unit = { |
39 | + 'unit': name, |
40 | + } |
41 | + unit.update(_get_juju_agent_status(obj)) |
42 | + if status is not None: |
43 | + unit.update(_get_model_details(status)) |
44 | + if machine is not None and machine_version is not None: |
45 | + unit['machine'] = machine |
46 | + unit['machine-version'] = machine_version |
47 | + return unit |
48 | + |
49 | + |
50 | +def _get_machines(status): |
51 | + if 'machines' in status: |
52 | + for m in status['machines']: |
53 | + yield _get_unit(m, status['machines'][m], status) |
54 | + |
55 | + |
56 | +def _get_units(status, machine): |
57 | + if 'applications' in status: |
58 | + apps = status['applications'] |
59 | + for a in apps: |
60 | + if 'units' in apps[a]: |
61 | + units = apps[a]['units'] |
62 | + for u in units: |
63 | + m = units[u].get('machine', None) |
64 | + if m == machine['unit']: |
65 | + machine_version = machine['version'] |
66 | + yield _get_unit(u, units[u], status, m, machine_version) |
67 | + if 'subordinates' in units[u]: |
68 | + for s in units[u]['subordinates']: |
69 | + yield _get_unit(s, units[u]['subordinates'][s], status, m, machine_version) |
70 | + |
71 | + |
72 | +def _get_model_machines(status): |
73 | + """Construct a list of machines in the model, where each machine is composed of an array of units. |
74 | + The first unit in the array is the machine itself, the remainder are the application units.""" |
75 | + machines = [] |
76 | + for m in _get_machines(status): |
77 | + machines.append([m] + list(_get_units(status, m))) |
78 | + return machines |
79 | + |
80 | + |
81 | +def _run(command, obj=None): |
82 | + juju_utils.debug('Running: ' + ' '.join(command)) |
83 | + if obj is not None: |
84 | + stdin = json.dumps(obj).encode() |
85 | + else: |
86 | + stdin = None |
87 | + |
88 | + proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
89 | + try: |
90 | + output, errors = proc.communicate(input=stdin, timeout=60) |
91 | + except subprocess.TimeoutExpired: |
92 | + proc.kill() |
93 | + output, errors = proc.communicate() |
94 | + if len(output): |
95 | + return json.loads(output.decode()) |
96 | + else: |
97 | + return None |
98 | + |
99 | + |
100 | +def _script_paths(location): |
101 | + loc = os.path.dirname(location) |
102 | + for path in glob.glob(os.path.join(loc, 'juju-*')): |
103 | + yield path |
104 | + for path in glob.glob(os.path.join(loc, 'juju_*.py')): |
105 | + yield path |
106 | + |
107 | + |
108 | +def check_model(status, formatter, fix=False, restart=False, ssh=True, show_all=False): |
109 | + """Check/fix agents on all machines in the model according to the parameters. |
110 | + Return the number of unhealthy units. |
111 | + """ |
112 | + allmachines = _get_model_machines(status) |
113 | + count = 0 |
114 | + |
115 | + for units in sorted(allmachines, key=lambda x: int(x[0]['unit'])): |
116 | + machinenum = units[0]['unit'] |
117 | + units[0]['fix'] = fix |
118 | + units[0]['restart'] = restart |
119 | + |
120 | + # pre-fill known unit fields |
121 | + for u in units: |
122 | + u['statustime'] = juju_utils.format_time('agent-time', u) |
123 | + u['cause'] = 'NOT CHECKED' |
124 | + |
125 | + # copy scripts to machine, ssh to machine, execute script with machine data as input |
126 | + if ssh and not juju_utils.is_good_machine(units, status): |
127 | + _run(['juju', 'ssh', machinenum, 'mkdir', '-p', '~/juju-upgrader']) |
128 | + # Clean up before we copy files for the upgrade. |
129 | + _run(['juju', 'ssh', machinenum, 'rm', '-f', '~/juju-upgrader/*']) |
130 | + _run(['juju', 'scp'] + list(_script_paths(sys.argv[0])) + [machinenum + ':~/juju-upgrader/']) |
131 | + result = _run(['juju', 'ssh', '--pty=false', machinenum, 'sudo ~/juju-upgrader/juju-machine-check'], units) |
132 | + if result is None: |
133 | + # some error occurred while running the script - there should be output from run() already |
134 | + break |
135 | + else: |
136 | + juju_utils.debug('Skipped ssh to machine ' + machinenum) |
137 | + result = units |
138 | + |
139 | + for u in result: |
140 | + u['logtime'] = juju_utils.format_time('logfile-time', u) |
141 | + u['proctime'] = juju_utils.format_time('start-time', u) |
142 | + good = juju_utils.is_good_unit(u, status) |
143 | + if show_all or not good: |
144 | + formatter.emit_record(u) |
145 | + if not good: |
146 | + count += 1 |
147 | + |
148 | + return count |
149 | diff --git a/unit_tests/test_juju_statistics.py b/unit_tests/test_juju_statistics.py |
150 | index fa16ffd..ccaa96f 100644 |
151 | --- a/unit_tests/test_juju_statistics.py |
152 | +++ b/unit_tests/test_juju_statistics.py |
153 | @@ -12,6 +12,8 @@ import unittest |
154 | |
155 | import juju_statistics |
156 | |
157 | +from unit_tests.testdata import test_statuses |
158 | + |
159 | |
160 | class TestJujuStatistics(unittest.TestCase): |
161 | |
162 | @@ -83,215 +85,7 @@ class TestJujuStatistics(unittest.TestCase): |
163 | self.stats.add_status({}) |
164 | self.assertEqual(self.stats.totals, {'applications': 0, 'machines': 0}) |
165 | self.assertEqual(self.stats.summary, {}) |
166 | - test_status_obj = { |
167 | - "model": { |
168 | - "name": "mojo-dot-canonical-dot-com", |
169 | - "controller": "prodstack", |
170 | - "cloud": "prodstack", |
171 | - "region": "prodstack", |
172 | - "version": "2.2.8", |
173 | - }, |
174 | - "machines": { |
175 | - "0": { |
176 | - "juju-status": { |
177 | - "current": "started", |
178 | - "since": "02 Jan 2018 20:23:12Z", |
179 | - "version": "2.2.8", |
180 | - }, |
181 | - "dns-name": "162.213.33.51", |
182 | - "ip-addresses": ["162.213.33.51", "10.0.0.10"], |
183 | - "instance-id": "68da26e2-9593-4ca7-9718-cda2dff6ceb1", |
184 | - "machine-status": { |
185 | - "current": "running", |
186 | - "message": "ACTIVE", |
187 | - "since": "30 Sep 2016 17:04:30Z", |
188 | - }, |
189 | - "series": "xenial", |
190 | - "hardware": "arch=amd64 cores=1 mem=2048M root-disk=10240M availability-zone=nova", |
191 | - } |
192 | - }, |
193 | - "applications": { |
194 | - "apache2": { |
195 | - "charm": "local:xenial/apache2-0", |
196 | - "series": "xenial", |
197 | - "os": "ubuntu", |
198 | - "charm-origin": "local", |
199 | - "charm-name": "apache2", |
200 | - "charm-rev": 0, |
201 | - "exposed": True, |
202 | - "application-status": { |
203 | - "current": "unknown", |
204 | - "since": "30 Sep 2016 17:07:54Z", |
205 | - }, |
206 | - "relations": { |
207 | - "juju-info": ["content-fetcher", "livepatch", "landscape"], |
208 | - "nrpe-external-master": ["nrpe"], |
209 | - }, |
210 | - "units": { |
211 | - "apache2/0": { |
212 | - "workload-status": { |
213 | - "current": "unknown", |
214 | - "since": "30 Sep 2016 17:07:54Z", |
215 | - }, |
216 | - "juju-status": { |
217 | - "current": "idle", |
218 | - "since": "04 Jan 2018 09:10:28Z", |
219 | - "version": "2.2.8", |
220 | - }, |
221 | - "leader": True, |
222 | - "machine": "0", |
223 | - "open-ports": ["80/tcp", "443/tcp"], |
224 | - "public-address": "162.213.33.51", |
225 | - "subordinates": { |
226 | - "content-fetcher/0": { |
227 | - "workload-status": { |
228 | - "current": "unknown", |
229 | - "since": "30 Sep 2016 17:28:40Z", |
230 | - }, |
231 | - "juju-status": { |
232 | - "current": "idle", |
233 | - "since": "04 Jan 2018 09:15:47Z", |
234 | - "version": "2.2.8", |
235 | - }, |
236 | - "leader": True, |
237 | - "upgrading-from": "local:xenial/content-fetcher-0", |
238 | - "public-address": "162.213.33.51", |
239 | - }, |
240 | - "livepatch/0": { |
241 | - "workload-status": { |
242 | - "current": "active", |
243 | - "message": "Effective kernel 4.4.0-103-generic", |
244 | - "since": "04 Jan 2018 09:14:48Z", |
245 | - }, |
246 | - "juju-status": { |
247 | - "current": "idle", |
248 | - "since": "04 Jan 2018 09:14:48Z", |
249 | - "version": "2.2.8", |
250 | - }, |
251 | - "leader": True, |
252 | - "upgrading-from": "local:xenial/livepatch-5", |
253 | - "public-address": "162.213.33.51", |
254 | - }, |
255 | - "landscape/1": { |
256 | - "workload-status": { |
257 | - "current": "active", |
258 | - "message": "System successfully registered", |
259 | - "since": "02 Jan 2018 20:24:58Z", |
260 | - }, |
261 | - "juju-status": { |
262 | - "current": "idle", |
263 | - "since": "04 Jan 2018 09:10:41Z", |
264 | - "version": "2.2.8", |
265 | - }, |
266 | - "leader": True, |
267 | - "upgrading-from": "local:xenial/landscape-client-23", |
268 | - "public-address": "162.213.33.51", |
269 | - }, |
270 | - "nrpe/0": { |
271 | - "workload-status": { |
272 | - "current": "unknown", |
273 | - "since": "30 Sep 2016 17:29:04Z", |
274 | - }, |
275 | - "juju-status": { |
276 | - "current": "idle", |
277 | - "since": "04 Jan 2018 09:16:01Z", |
278 | - "version": "2.2.8", |
279 | - }, |
280 | - "leader": True, |
281 | - "upgrading-from": "local:xenial/nrpe-0", |
282 | - "public-address": "162.213.33.51", |
283 | - } |
284 | - } |
285 | - } |
286 | - } |
287 | - }, |
288 | - "content-fetcher": { |
289 | - "charm": "local:xenial/content-fetcher-0", |
290 | - "series": "xenial", |
291 | - "os": "ubuntu", |
292 | - "charm-origin": "local", |
293 | - "charm-name": "content-fetcher", |
294 | - "charm-rev": 0, |
295 | - "exposed": False, |
296 | - "application-status": { |
297 | - "current": "unknown", |
298 | - "since": "30 Sep 2016 17:28:40Z", |
299 | - }, |
300 | - "relations": { |
301 | - "general-info": ["apache2"], |
302 | - }, |
303 | - "subordinate-to": ["apache2"], |
304 | - }, |
305 | - "livepatch": { |
306 | - "charm": "local:xenial/livepatch-5", |
307 | - "series": "xenial", |
308 | - "os": "ubuntu", |
309 | - "charm-origin": "local", |
310 | - "charm-name": "livepatch", |
311 | - "charm-rev": 5, |
312 | - "exposed": False, |
313 | - "application-status": { |
314 | - "current": "active", |
315 | - "message": "Effective kernel 4.4.0-103-generic", |
316 | - "since": "04 Jan 2018 09:14:48Z", |
317 | - }, |
318 | - "relations": { |
319 | - "general-info": ["apache2"], |
320 | - }, |
321 | - "subordinate-to": ["apache2"], |
322 | - "version": "1.2.31", |
323 | - }, |
324 | - "landscape": { |
325 | - "charm": "local:xenial/landscape-client-23", |
326 | - "series": "xenial", |
327 | - "os": "ubuntu", |
328 | - "charm-origin": "local", |
329 | - "charm-name": "landscape-client", |
330 | - "charm-rev": 23, |
331 | - "exposed": False, |
332 | - "application-status": { |
333 | - "current": "active", |
334 | - "message": "System successfully registered", |
335 | - "since": "02 Jan 2018 20:24:58Z", |
336 | - }, |
337 | - "relations": { |
338 | - "container": ["apache2"], |
339 | - }, |
340 | - "subordinate-to": ["apache2"], |
341 | - }, |
342 | - "nrpe": { |
343 | - "charm": "local:xenial/nrpe-0", |
344 | - "series": "xenial", |
345 | - "os": "ubuntu", |
346 | - "charm-origin": "local", |
347 | - "charm-name": "nrpe", |
348 | - "charm-rev": 0, |
349 | - "exposed": False, |
350 | - "application-status": { |
351 | - "current": "unknown", |
352 | - "since": "30 Sep 2016 17:29:04Z", |
353 | - }, |
354 | - "relations": { |
355 | - "nrpe-external-master": ["apache2"], |
356 | - }, |
357 | - "subordinate-to": ["apache2"], |
358 | - }, |
359 | - "ubuntu": { |
360 | - "charm": "cs:ubuntu-10", |
361 | - "series": "xenial", |
362 | - "os": "ubuntu", |
363 | - "charm-origin": "jujucharms", |
364 | - "charm-name": "ubuntu", |
365 | - "charm-rev": 10, |
366 | - "exposed": False, |
367 | - "application-status": { |
368 | - "current": "waiting", |
369 | - "message": "waiting for machine", |
370 | - "since": "24 Apr 2017 17:41:29Z", |
371 | - } |
372 | - } |
373 | - } |
374 | - } |
375 | + test_status_obj = test_statuses.MOJO_DOT_CANONICAL_DOT_COM_STATUS |
376 | self.stats.add_status(test_status_obj) |
377 | self.assertEqual(self.stats.totals, {'units': 5, 'applications': 6, 'machines': 1}) |
378 | expected_summary = { |
379 | diff --git a/unit_tests/test_juju_status.py b/unit_tests/test_juju_status.py |
380 | new file mode 100644 |
381 | index 0000000..44bb686 |
382 | --- /dev/null |
383 | +++ b/unit_tests/test_juju_status.py |
384 | @@ -0,0 +1,288 @@ |
385 | +#!/usr/bin/python3 |
386 | + |
387 | +# Copyright (c) 2018 Canonical Ltd |
388 | +# License: GPLv3 |
389 | +# Author: Tom Haddon <tom.haddon@canonical.com> |
390 | +# |
391 | +# Unit tests for juju_status.py. |
392 | +# |
393 | + |
394 | +import mock |
395 | +import unittest |
396 | +import sys |
397 | + |
398 | +import juju_status |
399 | + |
400 | +from unit_tests.testdata import test_statuses |
401 | + |
402 | + |
403 | +class TestJujuStatus(unittest.TestCase): |
404 | + |
405 | + def setUp(self): |
406 | + self.status = test_statuses.MOJO_DOT_CANONICAL_DOT_COM_STATUS |
407 | + |
408 | + def test__get_model_details(self): |
409 | + expected_model_details = {'model': 'mojo-dot-canonical-dot-com', 'model-version': '2.2.8'} |
410 | + self.assertEqual(juju_status._get_model_details(self.status), expected_model_details) |
411 | + |
412 | + def test__get_machines(self): |
413 | + expected_machines = { |
414 | + 'unit': '0', |
415 | + 'version': '2.2.8', |
416 | + 'since': '02 Jan 2018 20:23:12Z', |
417 | + 'model': 'mojo-dot-canonical-dot-com', |
418 | + 'model-version': '2.2.8', |
419 | + 'agent-time': 1514920992.0, |
420 | + 'current': 'started', |
421 | + } |
422 | + # Since this is a generator, list-ify it first so we can see results. |
423 | + self.assertEqual(list(juju_status._get_machines(self.status)), [expected_machines]) |
424 | + |
425 | + def test__get_juju_agent_status(self): |
426 | + expected_juju_agent_status = { |
427 | + 'agent-time': 1515053428.0, |
428 | + 'current': 'idle', |
429 | + 'since': '04 Jan 2018 09:10:28Z', |
430 | + 'version': '2.2.8', |
431 | + } |
432 | + self.assertEqual(juju_status._get_juju_agent_status( |
433 | + self.status['applications']['apache2']['units']['apache2/0']), expected_juju_agent_status) |
434 | + # Confirm if we pass an object that doesn't contain 'juju-status' we |
435 | + # get an empty dictionary. |
436 | + self.assertEqual(juju_status._get_juju_agent_status({'huhu-status': 'OK'}), {}) |
437 | + |
438 | + def test__get_unit(self): |
439 | + # Passing all arguments. |
440 | + actual_unit = juju_status._get_unit( |
441 | + 'apache2/0', |
442 | + self.status['applications']['apache2']['units']['apache2/0'], |
443 | + self.status, |
444 | + '0', |
445 | + '2.2.8', |
446 | + ) |
447 | + expected_unit = { |
448 | + 'agent-time': 1515053428.0, |
449 | + 'current': 'idle', |
450 | + 'machine': '0', |
451 | + 'machine-version': '2.2.8', |
452 | + 'model': 'mojo-dot-canonical-dot-com', |
453 | + 'model-version': '2.2.8', |
454 | + 'since': '04 Jan 2018 09:10:28Z', |
455 | + 'unit': 'apache2/0', |
456 | + 'version': '2.2.8', |
457 | + } |
458 | + self.assertEqual(actual_unit, expected_unit) |
459 | + # Not passing machine or machine version. |
460 | + actual_unit = juju_status._get_unit( |
461 | + 'apache2/0', |
462 | + self.status['applications']['apache2']['units']['apache2/0'], |
463 | + self.status) |
464 | + expected_unit = { |
465 | + 'agent-time': 1515053428.0, |
466 | + 'current': 'idle', |
467 | + 'model': 'mojo-dot-canonical-dot-com', |
468 | + 'model-version': '2.2.8', |
469 | + 'since': '04 Jan 2018 09:10:28Z', |
470 | + 'unit': 'apache2/0', |
471 | + 'version': '2.2.8', |
472 | + } |
473 | + self.assertEqual(actual_unit, expected_unit) |
474 | + # Not passing machine (should be treated as same as not passing |
475 | + # machine or machine version). |
476 | + actual_unit = juju_status._get_unit( |
477 | + 'apache2/0', |
478 | + self.status['applications']['apache2']['units']['apache2/0'], |
479 | + self.status, |
480 | + machine_version='2.2.8', |
481 | + ) |
482 | + self.assertEqual(actual_unit, expected_unit) |
483 | + # Not passing machine version (should be treated as same as not |
484 | + # passing machine or machine version). |
485 | + actual_unit = juju_status._get_unit( |
486 | + 'apache2/0', |
487 | + self.status['applications']['apache2']['units']['apache2/0'], |
488 | + self.status, |
489 | + '0', |
490 | + ) |
491 | + self.assertEqual(actual_unit, expected_unit) |
492 | + # Not passing status, machine or machine version. |
493 | + actual_unit = juju_status._get_unit( |
494 | + 'apache2/0', |
495 | + self.status['applications']['apache2']['units']['apache2/0']) |
496 | + expected_unit = { |
497 | + 'agent-time': 1515053428.0, |
498 | + 'current': 'idle', |
499 | + 'since': '04 Jan 2018 09:10:28Z', |
500 | + 'unit': 'apache2/0', |
501 | + 'version': '2.2.8', |
502 | + } |
503 | + self.assertEqual(actual_unit, expected_unit) |
504 | + |
505 | + def test__get_units(self): |
506 | + expected_units = [ |
507 | + { |
508 | + 'machine-version': '2.2.8', |
509 | + 'machine': '0', |
510 | + 'model': 'mojo-dot-canonical-dot-com', |
511 | + 'current': 'idle', |
512 | + 'model-version': '2.2.8', |
513 | + 'since': '04 Jan 2018 09:10:41Z', |
514 | + 'version': '2.2.8', |
515 | + 'unit': 'landscape/1', |
516 | + 'agent-time': 1515053441.0, |
517 | + }, |
518 | + { |
519 | + 'model-version': '2.2.8', |
520 | + 'unit': 'content-fetcher/0', |
521 | + 'model': 'mojo-dot-canonical-dot-com', |
522 | + 'machine-version': '2.2.8', |
523 | + 'version': '2.2.8', |
524 | + 'agent-time': 1515053747.0, |
525 | + 'machine': '0', |
526 | + 'since': '04 Jan 2018 09:15:47Z', |
527 | + 'current': 'idle', |
528 | + }, |
529 | + { |
530 | + 'machine': '0', |
531 | + 'current': 'idle', |
532 | + 'unit': 'nrpe/0', |
533 | + 'since': '04 Jan 2018 09:16:01Z', |
534 | + 'agent-time': 1515053761.0, |
535 | + 'version': '2.2.8', |
536 | + 'machine-version': '2.2.8', |
537 | + 'model': 'mojo-dot-canonical-dot-com', |
538 | + 'model-version': '2.2.8', |
539 | + }, |
540 | + { |
541 | + 'agent-time': 1515053428.0, |
542 | + 'model': 'mojo-dot-canonical-dot-com', |
543 | + 'machine': '0', |
544 | + 'model-version': '2.2.8', |
545 | + 'current': 'idle', |
546 | + 'machine-version': '2.2.8', |
547 | + 'unit': 'apache2/0', |
548 | + 'since': '04 Jan 2018 09:10:28Z', |
549 | + 'version': '2.2.8', |
550 | + }, |
551 | + { |
552 | + 'model': 'mojo-dot-canonical-dot-com', |
553 | + 'machine-version': '2.2.8', |
554 | + 'current': 'idle', |
555 | + 'machine': '0', |
556 | + 'version': '2.2.8', |
557 | + 'agent-time': 1515053688.0, |
558 | + 'model-version': '2.2.8', |
559 | + 'since': '04 Jan 2018 09:14:48Z', |
560 | + 'unit': 'livepatch/0', |
561 | + } |
562 | + ] |
563 | + machine = { |
564 | + 'unit': '0', |
565 | + 'version': '2.2.8', |
566 | + 'since': '02 Jan 2018 20:23:12Z', |
567 | + 'model': 'mojo-dot-canonical-dot-com', |
568 | + 'model-version': '2.2.8', |
569 | + 'agent-time': 1514920992.0, |
570 | + 'current': 'started'} |
571 | + # Since this is a generator, list-ify it first so we can see results. |
572 | + actual_units = list(juju_status._get_units(self.status, machine)) |
573 | + for unit in expected_units: |
574 | + self.assertTrue(unit in actual_units) |
575 | + self.assertEqual(len(expected_units), len(actual_units)) |
576 | + |
577 | + def test__get_model_machines(self): |
578 | + expected_model_machines = [ |
579 | + [ |
580 | + { |
581 | + 'agent-time': 1514920992.0, |
582 | + 'since': '02 Jan 2018 20:23:12Z', |
583 | + 'model': 'mojo-dot-canonical-dot-com', |
584 | + 'unit': '0', |
585 | + 'model-version': '2.2.8', |
586 | + 'current': 'started', |
587 | + 'version': '2.2.8', |
588 | + }, |
589 | + { |
590 | + 'agent-time': 1515053428.0, |
591 | + 'since': '04 Jan 2018 09:10:28Z', |
592 | + 'machine': '0', |
593 | + 'model': 'mojo-dot-canonical-dot-com', |
594 | + 'machine-version': '2.2.8', |
595 | + 'unit': 'apache2/0', |
596 | + 'model-version': '2.2.8', |
597 | + 'current': 'idle', |
598 | + 'version': '2.2.8', |
599 | + }, |
600 | + { |
601 | + 'agent-time': 1515053441.0, |
602 | + 'since': '04 Jan 2018 09:10:41Z', |
603 | + 'machine': '0', |
604 | + 'model': 'mojo-dot-canonical-dot-com', |
605 | + 'machine-version': '2.2.8', |
606 | + 'unit': 'landscape/1', |
607 | + 'model-version': '2.2.8', |
608 | + 'current': 'idle', |
609 | + 'version': '2.2.8', |
610 | + }, |
611 | + { |
612 | + 'agent-time': 1515053761.0, |
613 | + 'since': '04 Jan 2018 09:16:01Z', |
614 | + 'machine': '0', |
615 | + 'model': 'mojo-dot-canonical-dot-com', |
616 | + 'machine-version': '2.2.8', |
617 | + 'unit': 'nrpe/0', |
618 | + 'model-version': '2.2.8', |
619 | + 'current': 'idle', |
620 | + 'version': '2.2.8', |
621 | + }, |
622 | + { |
623 | + 'agent-time': 1515053747.0, |
624 | + 'since': '04 Jan 2018 09:15:47Z', |
625 | + 'machine': '0', |
626 | + 'model': 'mojo-dot-canonical-dot-com', |
627 | + 'machine-version': '2.2.8', |
628 | + 'unit': 'content-fetcher/0', |
629 | + 'model-version': '2.2.8', |
630 | + 'current': 'idle', |
631 | + 'version': '2.2.8', |
632 | + }, |
633 | + { |
634 | + 'agent-time': 1515053688.0, |
635 | + 'since': '04 Jan 2018 09:14:48Z', |
636 | + 'machine': '0', |
637 | + 'model': 'mojo-dot-canonical-dot-com', |
638 | + 'machine-version': '2.2.8', |
639 | + 'unit': 'livepatch/0', |
640 | + 'model-version': '2.2.8', |
641 | + 'current': 'idle', |
642 | + 'version': '2.2.8', |
643 | + } |
644 | + ] |
645 | + ] |
646 | + actual_model_machines = juju_status._get_model_machines(self.status) |
647 | + self.assertEqual(len(actual_model_machines), len(expected_model_machines)) |
648 | + for index, model_machine in enumerate(expected_model_machines): |
649 | + self.assertEqual(len(actual_model_machines[index]), len(expected_model_machines[index])) |
650 | + for model in expected_model_machines[index]: |
651 | + self.assertTrue(model in actual_model_machines[index]) |
652 | + |
653 | + @mock.patch('subprocess.Popen') |
654 | + def test__run(self, _popen): |
655 | + class Proc(object): |
656 | + def communicate(self, input=None, timeout=None): |
657 | + return b'{"test": 1234}', 0 |
658 | + |
659 | + proc = Proc() |
660 | + _popen.return_value = proc |
661 | + result = juju_status._run("ls") |
662 | + self.assertEqual(result, {"test": 1234}) |
663 | + |
664 | + def test__script_paths(self): |
665 | + expected_scripts = [ |
666 | + 'juju_status.py', |
667 | + 'juju_statistics.py', |
668 | + 'juju_formatter.py', |
669 | + 'juju_diagnostics.py', |
670 | + 'juju_utils.py', |
671 | + ] |
672 | + self.assertEqual(list(juju_status._script_paths(sys.argv[0])), expected_scripts) |
673 | diff --git a/unit_tests/testdata/test_statuses.py b/unit_tests/testdata/test_statuses.py |
674 | new file mode 100644 |
675 | index 0000000..10fe22e |
676 | --- /dev/null |
677 | +++ b/unit_tests/testdata/test_statuses.py |
678 | @@ -0,0 +1,218 @@ |
679 | +#!/usr/bin/python3 |
680 | + |
681 | +# Copyright (c) 2018 Canonical Ltd |
682 | +# License: GPLv3 |
683 | +# Author: Tom Haddon <tom.haddon@canonical.com> |
684 | +# |
685 | +# Test data for juju-upgrader. |
686 | +# |
687 | + |
688 | +MOJO_DOT_CANONICAL_DOT_COM_STATUS = { |
689 | + "model": { |
690 | + "name": "mojo-dot-canonical-dot-com", |
691 | + "controller": "prodstack", |
692 | + "cloud": "prodstack", |
693 | + "region": "prodstack", |
694 | + "version": "2.2.8", |
695 | + }, |
696 | + "machines": { |
697 | + "0": { |
698 | + "juju-status": { |
699 | + "current": "started", |
700 | + "since": "02 Jan 2018 20:23:12Z", |
701 | + "version": "2.2.8", |
702 | + }, |
703 | + "dns-name": "162.213.33.51", |
704 | + "ip-addresses": ["162.213.33.51", "10.0.0.10"], |
705 | + "instance-id": "68da26e2-9593-4ca7-9718-cda2dff6ceb1", |
706 | + "machine-status": { |
707 | + "current": "running", |
708 | + "message": "ACTIVE", |
709 | + "since": "30 Sep 2016 17:04:30Z", |
710 | + }, |
711 | + "series": "xenial", |
712 | + "hardware": "arch=amd64 cores=1 mem=2048M root-disk=10240M availability-zone=nova", |
713 | + } |
714 | + }, |
715 | + "applications": { |
716 | + "apache2": { |
717 | + "charm": "local:xenial/apache2-0", |
718 | + "series": "xenial", |
719 | + "os": "ubuntu", |
720 | + "charm-origin": "local", |
721 | + "charm-name": "apache2", |
722 | + "charm-rev": 0, |
723 | + "exposed": True, |
724 | + "application-status": { |
725 | + "current": "unknown", |
726 | + "since": "30 Sep 2016 17:07:54Z", |
727 | + }, |
728 | + "relations": { |
729 | + "juju-info": ["content-fetcher", "livepatch", "landscape"], |
730 | + "nrpe-external-master": ["nrpe"], |
731 | + }, |
732 | + "units": { |
733 | + "apache2/0": { |
734 | + "workload-status": { |
735 | + "current": "unknown", |
736 | + "since": "30 Sep 2016 17:07:54Z", |
737 | + }, |
738 | + "juju-status": { |
739 | + "current": "idle", |
740 | + "since": "04 Jan 2018 09:10:28Z", |
741 | + "version": "2.2.8", |
742 | + }, |
743 | + "leader": True, |
744 | + "machine": "0", |
745 | + "open-ports": ["80/tcp", "443/tcp"], |
746 | + "public-address": "162.213.33.51", |
747 | + "subordinates": { |
748 | + "content-fetcher/0": { |
749 | + "workload-status": { |
750 | + "current": "unknown", |
751 | + "since": "30 Sep 2016 17:28:40Z", |
752 | + }, |
753 | + "juju-status": { |
754 | + "current": "idle", |
755 | + "since": "04 Jan 2018 09:15:47Z", |
756 | + "version": "2.2.8", |
757 | + }, |
758 | + "leader": True, |
759 | + "upgrading-from": "local:xenial/content-fetcher-0", |
760 | + "public-address": "162.213.33.51", |
761 | + }, |
762 | + "livepatch/0": { |
763 | + "workload-status": { |
764 | + "current": "active", |
765 | + "message": "Effective kernel 4.4.0-103-generic", |
766 | + "since": "04 Jan 2018 09:14:48Z", |
767 | + }, |
768 | + "juju-status": { |
769 | + "current": "idle", |
770 | + "since": "04 Jan 2018 09:14:48Z", |
771 | + "version": "2.2.8", |
772 | + }, |
773 | + "leader": True, |
774 | + "upgrading-from": "local:xenial/livepatch-5", |
775 | + "public-address": "162.213.33.51", |
776 | + }, |
777 | + "landscape/1": { |
778 | + "workload-status": { |
779 | + "current": "active", |
780 | + "message": "System successfully registered", |
781 | + "since": "02 Jan 2018 20:24:58Z", |
782 | + }, |
783 | + "juju-status": { |
784 | + "current": "idle", |
785 | + "since": "04 Jan 2018 09:10:41Z", |
786 | + "version": "2.2.8", |
787 | + }, |
788 | + "leader": True, |
789 | + "upgrading-from": "local:xenial/landscape-client-23", |
790 | + "public-address": "162.213.33.51", |
791 | + }, |
792 | + "nrpe/0": { |
793 | + "workload-status": { |
794 | + "current": "unknown", |
795 | + "since": "30 Sep 2016 17:29:04Z", |
796 | + }, |
797 | + "juju-status": { |
798 | + "current": "idle", |
799 | + "since": "04 Jan 2018 09:16:01Z", |
800 | + "version": "2.2.8", |
801 | + }, |
802 | + "leader": True, |
803 | + "upgrading-from": "local:xenial/nrpe-0", |
804 | + "public-address": "162.213.33.51", |
805 | + } |
806 | + } |
807 | + } |
808 | + } |
809 | + }, |
810 | + "content-fetcher": { |
811 | + "charm": "local:xenial/content-fetcher-0", |
812 | + "series": "xenial", |
813 | + "os": "ubuntu", |
814 | + "charm-origin": "local", |
815 | + "charm-name": "content-fetcher", |
816 | + "charm-rev": 0, |
817 | + "exposed": False, |
818 | + "application-status": { |
819 | + "current": "unknown", |
820 | + "since": "30 Sep 2016 17:28:40Z", |
821 | + }, |
822 | + "relations": { |
823 | + "general-info": ["apache2"], |
824 | + }, |
825 | + "subordinate-to": ["apache2"], |
826 | + }, |
827 | + "livepatch": { |
828 | + "charm": "local:xenial/livepatch-5", |
829 | + "series": "xenial", |
830 | + "os": "ubuntu", |
831 | + "charm-origin": "local", |
832 | + "charm-name": "livepatch", |
833 | + "charm-rev": 5, |
834 | + "exposed": False, |
835 | + "application-status": { |
836 | + "current": "active", |
837 | + "message": "Effective kernel 4.4.0-103-generic", |
838 | + "since": "04 Jan 2018 09:14:48Z", |
839 | + }, |
840 | + "relations": { |
841 | + "general-info": ["apache2"], |
842 | + }, |
843 | + "subordinate-to": ["apache2"], |
844 | + "version": "1.2.31", |
845 | + }, |
846 | + "landscape": { |
847 | + "charm": "local:xenial/landscape-client-23", |
848 | + "series": "xenial", |
849 | + "os": "ubuntu", |
850 | + "charm-origin": "local", |
851 | + "charm-name": "landscape-client", |
852 | + "charm-rev": 23, |
853 | + "exposed": False, |
854 | + "application-status": { |
855 | + "current": "active", |
856 | + "message": "System successfully registered", |
857 | + "since": "02 Jan 2018 20:24:58Z", |
858 | + }, |
859 | + "relations": { |
860 | + "container": ["apache2"], |
861 | + }, |
862 | + "subordinate-to": ["apache2"], |
863 | + }, |
864 | + "nrpe": { |
865 | + "charm": "local:xenial/nrpe-0", |
866 | + "series": "xenial", |
867 | + "os": "ubuntu", |
868 | + "charm-origin": "local", |
869 | + "charm-name": "nrpe", |
870 | + "charm-rev": 0, |
871 | + "exposed": False, |
872 | + "application-status": { |
873 | + "current": "unknown", |
874 | + "since": "30 Sep 2016 17:29:04Z", |
875 | + }, |
876 | + "relations": { |
877 | + "nrpe-external-master": ["apache2"], |
878 | + }, |
879 | + "subordinate-to": ["apache2"], |
880 | + }, |
881 | + "ubuntu": { |
882 | + "charm": "cs:ubuntu-10", |
883 | + "series": "xenial", |
884 | + "os": "ubuntu", |
885 | + "charm-origin": "jujucharms", |
886 | + "charm-name": "ubuntu", |
887 | + "charm-rev": 10, |
888 | + "exposed": False, |
889 | + "application-status": { |
890 | + "current": "waiting", |
891 | + "message": "waiting for machine", |
892 | + "since": "24 Apr 2017 17:41:29Z", |
893 | + } |
894 | + } |
895 | + } |
896 | + } |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.