Merge lp:~ltrager/maas/lp1600328 into lp:~maas-committers/maas/trunk

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: no longer in the source branch.
Merged at revision: 5502
Proposed branch: lp:~ltrager/maas/lp1600328
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 145 lines (+72/-6)
5 files modified
src/maasserver/api/machines.py (+4/-1)
src/maasserver/api/tests/test_machines.py (+22/-0)
src/maasserver/models/node.py (+6/-3)
src/maasserver/models/tests/test_node.py (+36/-1)
src/maasserver/websockets/handlers/machine.py (+4/-1)
To merge this branch: bzr merge lp:~ltrager/maas/lp1600328
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+308981@code.launchpad.net

Commit message

Add a node event when no rack controller can access the BMC and handle failed defers.

Description of the change

When no rack controller can be found to access the nodes BMC an exception is raised. This exception is being thrown in a call back. The 500 error was being shown because nothing caught the error. The API and websocket now silently ignore that exception as this is now logged as a node power event. The power event is explicitly shown in the UI and the node is kept in a new state.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/api/machines.py'
2--- src/maasserver/api/machines.py 2016-10-20 08:41:30 +0000
3+++ src/maasserver/api/machines.py 2016-10-22 05:50:38 +0000
4@@ -941,7 +941,10 @@
5 """
6 machine = create_machine(request)
7 if request.user.is_superuser:
8- machine.accept_enlistment(request.user)
9+ d = machine.start_commissioning(request.user)
10+ # Silently ignore errors to prevent 500 errors. The commissioning
11+ # callbacks have their own logging. This fixes LP1600328.
12+ d.addErrback(lambda _: None)
13 return machine
14
15 def _check_system_ids_exist(self, system_ids):
16
17=== modified file 'src/maasserver/api/tests/test_machines.py'
18--- src/maasserver/api/tests/test_machines.py 2016-10-03 22:13:11 +0000
19+++ src/maasserver/api/tests/test_machines.py 2016-10-22 05:50:38 +0000
20@@ -204,6 +204,28 @@
21 "Select a valid choice. %s is not one of the "
22 "available choices." % power_type, validation_errors[0])
23
24+ def test_POST_handles_error_when_unable_to_access_bmc(self):
25+ # Regression test for LP1600328
26+ self.become_admin()
27+ power_address = factory.make_ip_address()
28+ power_id = factory.make_name('power_id')
29+ response = self.client.post(
30+ reverse('machines_handler'),
31+ {
32+ 'architecture': make_usable_architecture(self),
33+ 'mac_addresses': ['aa:bb:cc:dd:ee:ff'],
34+ 'power_type': 'virsh',
35+ 'power_parameters_power_address': power_address,
36+ 'power_parameters_power_id': power_id,
37+ })
38+ parsed_result = json.loads(
39+ response.content.decode(settings.DEFAULT_CHARSET))
40+ machine = Machine.objects.get(system_id=parsed_result['system_id'])
41+ self.assertEqual('virsh', parsed_result['power_type'])
42+ self.assertEqual(
43+ power_address, machine.power_parameters['power_address'])
44+ self.assertEqual(power_id, machine.power_parameters['power_id'])
45+
46 def test_GET_lists_machines(self):
47 # The api allows for fetching the list of Machines.
48 machine1 = factory.make_Node()
49
50=== modified file 'src/maasserver/models/node.py'
51--- src/maasserver/models/node.py 2016-10-13 00:11:49 +0000
52+++ src/maasserver/models/node.py 2016-10-22 05:50:38 +0000
53@@ -3084,9 +3084,12 @@
54 for rack in self.get_boot_rack_controllers()
55 ]
56 if len(client_idents) == 0 and len(fallback_idents) == 0:
57- raise PowerProblem(
58- "No rack controllers can access the BMC of node: %s" % (
59- self.hostname))
60+ err_msg = "No rack controllers can access the BMC of node: %s" % (
61+ self.hostname)
62+ self._register_request_event(
63+ self.owner, EVENT_TYPES.NODE_POWER_QUERY_FAILED,
64+ "Failed to query node's BMC", err_msg)
65+ raise PowerProblem(err_msg)
66 return client_idents, fallback_idents
67
68 @transactional
69
70=== modified file 'src/maasserver/models/tests/test_node.py'
71--- src/maasserver/models/tests/test_node.py 2016-10-18 17:35:20 +0000
72+++ src/maasserver/models/tests/test_node.py 2016-10-22 05:50:38 +0000
73@@ -51,7 +51,10 @@
74 POWER_STATE,
75 SERVICE_STATUS,
76 )
77-from maasserver.exceptions import NodeStateViolation
78+from maasserver.exceptions import (
79+ NodeStateViolation,
80+ PowerProblem,
81+)
82 from maasserver.models import (
83 bmc as bmc_module,
84 BondInterface,
85@@ -61,6 +64,7 @@
86 Controller,
87 Device,
88 Domain,
89+ EventType,
90 Fabric,
91 Interface,
92 LicenseKey,
93@@ -5026,6 +5030,37 @@
94 "deploy this node."], node.storage_layout_issues())
95
96
97+class TestGetBMCClientConnectionInfo(MAASServerTestCase):
98+
99+ def test__returns_bmc_identifiers(self):
100+ node = factory.make_Node()
101+
102+ mock_bmcs = self.patch(node.bmc, 'get_client_identifiers')
103+ fake_bmc_id = factory.make_name('system_id')
104+ mock_bmcs.return_value = [fake_bmc_id]
105+
106+ mock_fallbacks = self.patch(node, 'get_boot_rack_controllers')
107+ fake_fallback_id = factory.make_name('system_id')
108+ fallback = MagicMock()
109+ fallback.system_id = fake_fallback_id
110+ mock_fallbacks.return_value = [fallback]
111+
112+ self.assertEquals(
113+ ([fake_bmc_id], [fake_fallback_id]),
114+ node._get_bmc_client_connection_info())
115+
116+ def test__creates_event_on_error(self):
117+ node = factory.make_Node()
118+
119+ self.assertRaises(PowerProblem, node._get_bmc_client_connection_info)
120+ event_type = EventType.objects.get(
121+ name=EVENT_TYPES.NODE_POWER_QUERY_FAILED)
122+ event = node.event_set.get(type=event_type)
123+ self.assertEquals(
124+ ('No rack controllers can access the BMC of node: %s' %
125+ node.hostname), event.description)
126+
127+
128 class TestNode_Stop(MAASServerTestCase):
129 """Tests for Node.stop()."""
130
131
132=== modified file 'src/maasserver/websockets/handlers/machine.py'
133--- src/maasserver/websockets/handlers/machine.py 2016-10-21 01:19:51 +0000
134+++ src/maasserver/websockets/handlers/machine.py 2016-10-22 05:50:38 +0000
135@@ -265,7 +265,10 @@
136
137 # Start the commissioning process right away, which has the
138 # desired side effect of initializing the node's power state.
139- node_obj.start_commissioning(self.user)
140+ d = node_obj.start_commissioning(self.user)
141+ # Silently ignore errors to prevent tracebacks. The commissioning
142+ # callbacks have their own logging. This fixes LP1600328.
143+ d.addErrback(lambda _: None)
144
145 return self.full_dehydrate(node_obj)
146