Merge ~lloydwaltersj/maas:image-deployment-data into maas:master
- Git
- lp:~lloydwaltersj/maas
- image-deployment-data
- Merge into master
Status: | Merged |
---|---|
Approved by: | Jack Lloyd-Walters |
Approved revision: | 29626a824458f9ec2de1b6b2d1959cc5e5fc5c4c |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~lloydwaltersj/maas:image-deployment-data |
Merge into: | maas:master |
Diff against target: |
283 lines (+95/-19) 8 files modified
src/maasserver/api/boot_resources.py (+1/-0) src/maasserver/api/tests/test_boot_resources.py (+3/-0) src/maasserver/models/bootresource.py (+19/-13) src/maasserver/models/node.py (+11/-1) src/maasserver/models/tests/test_node.py (+5/-2) src/maasserver/websockets/handlers/bootresource.py (+26/-3) src/maasserver/websockets/handlers/tests/test_bootresource.py (+26/-0) src/provisioningserver/events.py (+4/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
Adam Collard (community) | Approve | ||
Review via email: mp+434563@code.launchpad.net |
Commit message
Add deployment information to boot resources
Description of the change
Jack Lloyd-Walters (lloydwaltersj) wrote : | # |
jenkins: !test
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: FAILED
LOG: http://
COMMIT: dc80b0ccfc25d1c
Adam Collard (adam-collard) : | # |
- d5ce368... by Jack Lloyd-Walters
-
Replace with events
- 5896d87... by Jack Lloyd-Walters
-
formatting and linting
- fc6df1e... by Jack Lloyd-Walters
-
add type hinting and linting
- 88e1b84... by Jack Lloyd-Walters
-
Fix tests raising errors
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: FAILED
LOG: http://
COMMIT: fc6df1e7bd9324a
- bdef783... by Jack Lloyd-Walters
-
fix status change test
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: SUCCESS
COMMIT: 88e1b84eb5da9cd
Adam Collard (adam-collard) : | # |
Adam Collard (adam-collard) : | # |
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: SUCCESS
COMMIT: bdef783c5d5dd1e
- 6054bb3... by Jack Lloyd-Walters
-
remove layering violation
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: SUCCESS
COMMIT: 6054bb33c2141e1
- 600e0f7... by Jack Lloyd-Walters
-
remove unused column from database
- 4448eb7... by Jack Lloyd-Walters
-
fix restAPI issues
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: FAILED
LOG: http://
COMMIT: 600e0f7bf050133
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: SUCCESS
COMMIT: 4448eb7bb7ee931
- f128bd4... by Jack Lloyd-Walters
-
respond to comments
- 29626a8... by Jack Lloyd-Walters
-
remove erroneous code
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b image-deploymen
STATUS: SUCCESS
COMMIT: 29626a824458f9e
Preview Diff
1 | diff --git a/src/maasserver/api/boot_resources.py b/src/maasserver/api/boot_resources.py | |||
2 | index 6100c89..770fdf1 100644 | |||
3 | --- a/src/maasserver/api/boot_resources.py | |||
4 | +++ b/src/maasserver/api/boot_resources.py | |||
5 | @@ -108,6 +108,7 @@ def boot_resource_to_dict(resource, with_sets=False): | |||
6 | 108 | "name": resource.name, | 108 | "name": resource.name, |
7 | 109 | "architecture": resource.architecture, | 109 | "architecture": resource.architecture, |
8 | 110 | "resource_uri": reverse("boot_resource_handler", args=[resource.id]), | 110 | "resource_uri": reverse("boot_resource_handler", args=[resource.id]), |
9 | 111 | "last_deployed": resource.get_last_deploy(), | ||
10 | 111 | } | 112 | } |
11 | 112 | dict_representation.update(resource.extra) | 113 | dict_representation.update(resource.extra) |
12 | 113 | if with_sets: | 114 | if with_sets: |
13 | diff --git a/src/maasserver/api/tests/test_boot_resources.py b/src/maasserver/api/tests/test_boot_resources.py | |||
14 | index a8ac72c..a4e0dc5 100644 | |||
15 | --- a/src/maasserver/api/tests/test_boot_resources.py | |||
16 | +++ b/src/maasserver/api/tests/test_boot_resources.py | |||
17 | @@ -101,6 +101,9 @@ class TestHelpers(APITestCase.ForUser): | |||
18 | 101 | get_boot_resource_uri(resource), | 101 | get_boot_resource_uri(resource), |
19 | 102 | dict_representation["resource_uri"], | 102 | dict_representation["resource_uri"], |
20 | 103 | ) | 103 | ) |
21 | 104 | self.assertEqual( | ||
22 | 105 | resource.get_last_deploy(), dict_representation["last_deployed"] | ||
23 | 106 | ) | ||
24 | 104 | self.assertFalse("sets" in dict_representation) | 107 | self.assertFalse("sets" in dict_representation) |
25 | 105 | 108 | ||
26 | 106 | def test_boot_resource_to_dict_with_sets(self): | 109 | def test_boot_resource_to_dict_with_sets(self): |
27 | diff --git a/src/maasserver/models/bootresource.py b/src/maasserver/models/bootresource.py | |||
28 | index 3ec9528..7fbb860 100644 | |||
29 | --- a/src/maasserver/models/bootresource.py | |||
30 | +++ b/src/maasserver/models/bootresource.py | |||
31 | @@ -1,9 +1,9 @@ | |||
33 | 1 | # Copyright 2014-2018 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2014-2022 Canonical Ltd. This software is licensed under the |
34 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
35 | 3 | 3 | ||
36 | 4 | """Boot Resource.""" | 4 | """Boot Resource.""" |
37 | 5 | 5 | ||
39 | 6 | 6 | from datetime import datetime | |
40 | 7 | from operator import attrgetter | 7 | from operator import attrgetter |
41 | 8 | 8 | ||
42 | 9 | from django.core.exceptions import ValidationError | 9 | from django.core.exceptions import ValidationError |
43 | @@ -16,7 +16,6 @@ from django.db.models import ( | |||
44 | 16 | Prefetch, | 16 | Prefetch, |
45 | 17 | Sum, | 17 | Sum, |
46 | 18 | ) | 18 | ) |
47 | 19 | from django.utils import timezone | ||
48 | 20 | 19 | ||
49 | 21 | from maasserver.enum import ( | 20 | from maasserver.enum import ( |
50 | 22 | BOOT_RESOURCE_FILE_TYPE, | 21 | BOOT_RESOURCE_FILE_TYPE, |
51 | @@ -516,16 +515,6 @@ class BootResource(CleanSave, TimestampedModel): | |||
52 | 516 | """Return rtype text as displayed to the user.""" | 515 | """Return rtype text as displayed to the user.""" |
53 | 517 | return BOOT_RESOURCE_TYPE_CHOICES_DICT[self.rtype] | 516 | return BOOT_RESOURCE_TYPE_CHOICES_DICT[self.rtype] |
54 | 518 | 517 | ||
55 | 519 | @property | ||
56 | 520 | def last_deployed(self) -> timezone.datetime: | ||
57 | 521 | """Returns the most recent time of deplyment for an image.""" | ||
58 | 522 | # Mock data: Generates a random time based on the hash of the | ||
59 | 523 | # resource name. | ||
60 | 524 | ms_py = 3153600000000 | ||
61 | 525 | return timezone.datetime(2022, 6, 1) + timezone.timedelta( | ||
62 | 526 | microseconds=hash(self.name) % ms_py | ||
63 | 527 | ) | ||
64 | 528 | |||
65 | 529 | def clean(self): | 518 | def clean(self): |
66 | 530 | """Validate the model. | 519 | """Validate the model. |
67 | 531 | 520 | ||
68 | @@ -591,6 +580,23 @@ class BootResource(CleanSave, TimestampedModel): | |||
69 | 591 | return resource_set | 580 | return resource_set |
70 | 592 | return None | 581 | return None |
71 | 593 | 582 | ||
72 | 583 | def get_last_deploy(self) -> datetime: | ||
73 | 584 | from maasserver.models.event import Event | ||
74 | 585 | from provisioningserver.events import EVENT_TYPES | ||
75 | 586 | |||
76 | 587 | deploy_msg = f"deployed {self.name}/{self.architecture}" | ||
77 | 588 | try: | ||
78 | 589 | return ( | ||
79 | 590 | Event.objects.filter( | ||
80 | 591 | type__name=EVENT_TYPES.IMAGE_DEPLOYED, | ||
81 | 592 | description=deploy_msg, | ||
82 | 593 | ) | ||
83 | 594 | .latest("created") | ||
84 | 595 | .created | ||
85 | 596 | ) | ||
86 | 597 | except Event.DoesNotExist: | ||
87 | 598 | pass | ||
88 | 599 | |||
89 | 594 | def split_arch(self): | 600 | def split_arch(self): |
90 | 595 | return self.architecture.split("/") | 601 | return self.architecture.split("/") |
91 | 596 | 602 | ||
92 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py | |||
93 | index e1c6923..e9766d6 100644 | |||
94 | --- a/src/maasserver/models/node.py | |||
95 | +++ b/src/maasserver/models/node.py | |||
96 | @@ -1,4 +1,4 @@ | |||
98 | 1 | # Copyright 2012-2021 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2012-2022 Canonical Ltd. This software is licensed under the |
99 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
100 | 3 | 3 | ||
101 | 4 | """Node objects.""" | 4 | """Node objects.""" |
102 | @@ -1789,11 +1789,21 @@ class Node(CleanSave, TimestampedModel): | |||
103 | 1789 | self.update_status(NODE_STATUS.DEPLOYED) | 1789 | self.update_status(NODE_STATUS.DEPLOYED) |
104 | 1790 | if self.enable_hw_sync: | 1790 | if self.enable_hw_sync: |
105 | 1791 | self.last_sync = datetime.now() | 1791 | self.last_sync = datetime.now() |
106 | 1792 | self.update_deployment_time() | ||
107 | 1792 | self.save() | 1793 | self.save() |
108 | 1793 | 1794 | ||
109 | 1794 | # Create a status message for DEPLOYED. | 1795 | # Create a status message for DEPLOYED. |
110 | 1795 | Event.objects.create_node_event(self, EVENT_TYPES.DEPLOYED) | 1796 | Event.objects.create_node_event(self, EVENT_TYPES.DEPLOYED) |
111 | 1796 | 1797 | ||
112 | 1798 | def update_deployment_time(self) -> None: | ||
113 | 1799 | from maasserver.models.event import Event | ||
114 | 1800 | |||
115 | 1801 | Event.objects.create_node_event( | ||
116 | 1802 | self, | ||
117 | 1803 | EVENT_TYPES.IMAGE_DEPLOYED, | ||
118 | 1804 | event_description=f"deployed {self.osystem}/{self.distro_series}/{self.architecture}", | ||
119 | 1805 | ) | ||
120 | 1806 | |||
121 | 1797 | def ip_addresses(self, ifaces=None): | 1807 | def ip_addresses(self, ifaces=None): |
122 | 1798 | """IP addresses allocated to this node. | 1808 | """IP addresses allocated to this node. |
123 | 1799 | 1809 | ||
124 | diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py | |||
125 | index 3aba41b..4cf6669 100644 | |||
126 | --- a/src/maasserver/models/tests/test_node.py | |||
127 | +++ b/src/maasserver/models/tests/test_node.py | |||
128 | @@ -4823,9 +4823,12 @@ class TestNode(MAASServerTestCase): | |||
129 | 4823 | self.disable_node_query() | 4823 | self.disable_node_query() |
130 | 4824 | node = factory.make_Node(status=NODE_STATUS.DEPLOYING) | 4824 | node = factory.make_Node(status=NODE_STATUS.DEPLOYING) |
131 | 4825 | node.end_deployment() | 4825 | node.end_deployment() |
133 | 4826 | event = Event.objects.get(node=node) | 4826 | events = Event.objects.filter(node=node) |
134 | 4827 | self.assertEqual(NODE_STATUS.DEPLOYED, reload_object(node).status) | 4827 | self.assertEqual(NODE_STATUS.DEPLOYED, reload_object(node).status) |
136 | 4828 | self.assertEqual(event.type.name, EVENT_TYPES.DEPLOYED) | 4828 | self.assertEqual( |
137 | 4829 | {event.type.name for event in events}, | ||
138 | 4830 | {EVENT_TYPES.IMAGE_DEPLOYED, EVENT_TYPES.DEPLOYED}, | ||
139 | 4831 | ) | ||
140 | 4829 | 4832 | ||
141 | 4830 | def test_end_deployment_sets_first_last_sync_value(self): | 4833 | def test_end_deployment_sets_first_last_sync_value(self): |
142 | 4831 | self.disable_node_query() | 4834 | self.disable_node_query() |
143 | diff --git a/src/maasserver/websockets/handlers/bootresource.py b/src/maasserver/websockets/handlers/bootresource.py | |||
144 | index 908af1e..fa4b17c 100644 | |||
145 | --- a/src/maasserver/websockets/handlers/bootresource.py | |||
146 | +++ b/src/maasserver/websockets/handlers/bootresource.py | |||
147 | @@ -5,6 +5,8 @@ | |||
148 | 5 | 5 | ||
149 | 6 | 6 | ||
150 | 7 | from collections import defaultdict | 7 | from collections import defaultdict |
151 | 8 | from datetime import datetime | ||
152 | 9 | from typing import Optional | ||
153 | 8 | 10 | ||
154 | 9 | from distro_info import UbuntuDistroInfo | 11 | from distro_info import UbuntuDistroInfo |
155 | 10 | from django.core.exceptions import ValidationError | 12 | from django.core.exceptions import ValidationError |
156 | @@ -376,7 +378,9 @@ class BootResourceHandler(Handler): | |||
157 | 376 | count += 1 | 378 | count += 1 |
158 | 377 | return count | 379 | return count |
159 | 378 | 380 | ||
161 | 379 | def pick_latest_datetime(self, time, other_time): | 381 | def pick_latest_datetime( |
162 | 382 | self, time: datetime, other_time: datetime | ||
163 | 383 | ) -> datetime: | ||
164 | 380 | """Return the datetime that is the latest.""" | 384 | """Return the datetime that is the latest.""" |
165 | 381 | if time is None: | 385 | if time is None: |
166 | 382 | return other_time | 386 | return other_time |
167 | @@ -410,7 +414,9 @@ class BootResourceHandler(Handler): | |||
168 | 410 | return False | 414 | return False |
169 | 411 | return True | 415 | return True |
170 | 412 | 416 | ||
172 | 413 | def get_last_update_for_resources(self, resources): | 417 | def get_last_update_for_resources( |
173 | 418 | self, resources: list[BootResource] | ||
174 | 419 | ) -> datetime: | ||
175 | 414 | """Return the latest updated time for all resources.""" | 420 | """Return the latest updated time for all resources.""" |
176 | 415 | last_update = None | 421 | last_update = None |
177 | 416 | for resource in resources: | 422 | for resource in resources: |
178 | @@ -431,6 +437,19 @@ class BootResourceHandler(Handler): | |||
179 | 431 | for resource in resources | 437 | for resource in resources |
180 | 432 | ) | 438 | ) |
181 | 433 | 439 | ||
182 | 440 | def get_last_deployed_for_resources( | ||
183 | 441 | self, resources: list[BootResource] | ||
184 | 442 | ) -> Optional[datetime]: | ||
185 | 443 | """Return the most recent deploy time for all resources.""" | ||
186 | 444 | last_deployed = None | ||
187 | 445 | for resource in resources: | ||
188 | 446 | this_deploy = resource.get_last_deploy() | ||
189 | 447 | if this_deploy is not None: | ||
190 | 448 | last_deployed = self.pick_latest_datetime( | ||
191 | 449 | last_deployed, this_deploy | ||
192 | 450 | ) | ||
193 | 451 | return last_deployed | ||
194 | 452 | |||
195 | 434 | def get_progress_for_resources(self, resources): | 453 | def get_progress_for_resources(self, resources): |
196 | 435 | """Return the overall progress for all resources.""" | 454 | """Return the overall progress for all resources.""" |
197 | 436 | size = 0 | 455 | size = 0 |
198 | @@ -479,6 +498,7 @@ class BootResourceHandler(Handler): | |||
199 | 479 | number_of_nodes = self.get_number_of_nodes_for_resources(group) | 498 | number_of_nodes = self.get_number_of_nodes_for_resources(group) |
200 | 480 | complete = self.are_all_resources_complete(group) | 499 | complete = self.are_all_resources_complete(group) |
201 | 481 | progress = self.get_progress_for_resources(group) | 500 | progress = self.get_progress_for_resources(group) |
202 | 501 | last_deployed = self.get_last_deployed_for_resources(group) | ||
203 | 482 | 502 | ||
204 | 483 | # Set the computed attributes on the first resource as that will | 503 | # Set the computed attributes on the first resource as that will |
205 | 484 | # be the only one returned to the UI. | 504 | # be the only one returned to the UI. |
206 | @@ -489,6 +509,7 @@ class BootResourceHandler(Handler): | |||
207 | 489 | resource.size = human_readable_bytes(unique_size) | 509 | resource.size = human_readable_bytes(unique_size) |
208 | 490 | resource.last_update = last_update | 510 | resource.last_update = last_update |
209 | 491 | resource.number_of_nodes = number_of_nodes | 511 | resource.number_of_nodes = number_of_nodes |
210 | 512 | resource.last_deployed = last_deployed | ||
211 | 492 | resource.complete = complete | 513 | resource.complete = complete |
212 | 493 | if not complete: | 514 | if not complete: |
213 | 494 | if progress > 0: | 515 | if progress > 0: |
214 | @@ -599,7 +620,9 @@ class BootResourceHandler(Handler): | |||
215 | 599 | ), | 620 | ), |
216 | 600 | "lastDeployed": resource.last_deployed.strftime( | 621 | "lastDeployed": resource.last_deployed.strftime( |
217 | 601 | "%a, %d %b. %Y %H:%M:%S" | 622 | "%a, %d %b. %Y %H:%M:%S" |
219 | 602 | ), | 623 | ) |
220 | 624 | if resource.last_deployed | ||
221 | 625 | else None, | ||
222 | 603 | } | 626 | } |
223 | 604 | for resource in self.combine_resources( | 627 | for resource in self.combine_resources( |
224 | 605 | BootResource.objects.filter(bootloader_type=None) | 628 | BootResource.objects.filter(bootloader_type=None) |
225 | diff --git a/src/maasserver/websockets/handlers/tests/test_bootresource.py b/src/maasserver/websockets/handlers/tests/test_bootresource.py | |||
226 | index d1f401b..96c773f 100644 | |||
227 | --- a/src/maasserver/websockets/handlers/tests/test_bootresource.py | |||
228 | +++ b/src/maasserver/websockets/handlers/tests/test_bootresource.py | |||
229 | @@ -364,6 +364,32 @@ class TestBootResourcePoll(MAASServerTestCase, PatchOSInfoMixin): | |||
230 | 364 | resource = response["resources"][0] | 364 | resource = response["resources"][0] |
231 | 365 | self.assertEqual(version, resource["title"]) | 365 | self.assertEqual(version, resource["title"]) |
232 | 366 | 366 | ||
233 | 367 | def test_shows_last_deployment_time(self) -> None: | ||
234 | 368 | owner = factory.make_admin() | ||
235 | 369 | handler = BootResourceHandler(owner, {}, None) | ||
236 | 370 | resource = factory.make_usable_boot_resource( | ||
237 | 371 | rtype=BOOT_RESOURCE_TYPE.SYNCED | ||
238 | 372 | ) | ||
239 | 373 | os_name, series = resource.name.split("/") | ||
240 | 374 | # The polled datetime only has granularity of order seconds | ||
241 | 375 | start_time = datetime.datetime.now().replace(microsecond=0) | ||
242 | 376 | node = factory.make_Node( | ||
243 | 377 | status=NODE_STATUS.DEPLOYED, | ||
244 | 378 | osystem=os_name, | ||
245 | 379 | distro_series=series, | ||
246 | 380 | architecture=resource.architecture, | ||
247 | 381 | ) | ||
248 | 382 | node.end_deployment() | ||
249 | 383 | response = handler.poll({}) | ||
250 | 384 | resource = response["resources"][0] | ||
251 | 385 | self.assertIn("lastDeployed", resource) | ||
252 | 386 | self.assertGreaterEqual( | ||
253 | 387 | datetime.datetime.strptime( | ||
254 | 388 | resource["lastDeployed"], "%a, %d %b. %Y %H:%M:%S" | ||
255 | 389 | ), | ||
256 | 390 | start_time, | ||
257 | 391 | ) | ||
258 | 392 | |||
259 | 367 | def test_shows_number_of_nodes_deployed_for_resource(self): | 393 | def test_shows_number_of_nodes_deployed_for_resource(self): |
260 | 368 | owner = factory.make_admin() | 394 | owner = factory.make_admin() |
261 | 369 | handler = BootResourceHandler(owner, {}, None) | 395 | handler = BootResourceHandler(owner, {}, None) |
262 | diff --git a/src/provisioningserver/events.py b/src/provisioningserver/events.py | |||
263 | index 6d6cceb..bffd6ae 100644 | |||
264 | --- a/src/provisioningserver/events.py | |||
265 | +++ b/src/provisioningserver/events.py | |||
266 | @@ -147,6 +147,7 @@ class EVENT_TYPES: | |||
267 | 147 | READY = "READY" | 147 | READY = "READY" |
268 | 148 | DEPLOYING = "DEPLOYING" | 148 | DEPLOYING = "DEPLOYING" |
269 | 149 | DEPLOYED = "DEPLOYED" | 149 | DEPLOYED = "DEPLOYED" |
270 | 150 | IMAGE_DEPLOYED = "IMAGE_DEPLOYED" | ||
271 | 150 | RELEASING = "RELEASING" | 151 | RELEASING = "RELEASING" |
272 | 151 | RELEASED = "RELEASED" | 152 | RELEASED = "RELEASED" |
273 | 152 | ENTERING_RESCUE_MODE = "ENTERING_RESCUE_MODE" | 153 | ENTERING_RESCUE_MODE = "ENTERING_RESCUE_MODE" |
274 | @@ -411,6 +412,9 @@ EVENT_DETAILS = { | |||
275 | 411 | EVENT_TYPES.READY: EventDetail(description="Ready", level=INFO), | 412 | EVENT_TYPES.READY: EventDetail(description="Ready", level=INFO), |
276 | 412 | EVENT_TYPES.DEPLOYING: EventDetail(description="Deploying", level=INFO), | 413 | EVENT_TYPES.DEPLOYING: EventDetail(description="Deploying", level=INFO), |
277 | 413 | EVENT_TYPES.DEPLOYED: EventDetail(description="Deployed", level=INFO), | 414 | EVENT_TYPES.DEPLOYED: EventDetail(description="Deployed", level=INFO), |
278 | 415 | EVENT_TYPES.IMAGE_DEPLOYED: EventDetail( | ||
279 | 416 | description="Image Deployed", level=INFO | ||
280 | 417 | ), | ||
281 | 414 | EVENT_TYPES.RELEASING: EventDetail(description="Releasing", level=INFO), | 418 | EVENT_TYPES.RELEASING: EventDetail(description="Releasing", level=INFO), |
282 | 415 | EVENT_TYPES.RELEASED: EventDetail(description="Released", level=INFO), | 419 | EVENT_TYPES.RELEASED: EventDetail(description="Released", level=INFO), |
283 | 416 | EVENT_TYPES.ENTERING_RESCUE_MODE: EventDetail( | 420 | EVENT_TYPES.ENTERING_RESCUE_MODE: EventDetail( |
UNIT TESTS t-data lp:~lloydwaltersj/maas/+git/maas into -b master lp:~maas-committers/maas
-b image-deploymen
STATUS: FAILED maas-ci. internal: 8080/job/ maas-tester/ 1616/consoleTex t 67530edf6d73d3e bbb7c46bd8
LOG: http://
COMMIT: 3a8d73cfb8628de