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
=== modified file 'src/maasserver/api/machines.py'
--- src/maasserver/api/machines.py 2016-10-20 08:41:30 +0000
+++ src/maasserver/api/machines.py 2016-10-22 05:50:38 +0000
@@ -941,7 +941,10 @@
941 """941 """
942 machine = create_machine(request)942 machine = create_machine(request)
943 if request.user.is_superuser:943 if request.user.is_superuser:
944 machine.accept_enlistment(request.user)944 d = machine.start_commissioning(request.user)
945 # Silently ignore errors to prevent 500 errors. The commissioning
946 # callbacks have their own logging. This fixes LP1600328.
947 d.addErrback(lambda _: None)
945 return machine948 return machine
946949
947 def _check_system_ids_exist(self, system_ids):950 def _check_system_ids_exist(self, system_ids):
948951
=== modified file 'src/maasserver/api/tests/test_machines.py'
--- src/maasserver/api/tests/test_machines.py 2016-10-03 22:13:11 +0000
+++ src/maasserver/api/tests/test_machines.py 2016-10-22 05:50:38 +0000
@@ -204,6 +204,28 @@
204 "Select a valid choice. %s is not one of the "204 "Select a valid choice. %s is not one of the "
205 "available choices." % power_type, validation_errors[0])205 "available choices." % power_type, validation_errors[0])
206206
207 def test_POST_handles_error_when_unable_to_access_bmc(self):
208 # Regression test for LP1600328
209 self.become_admin()
210 power_address = factory.make_ip_address()
211 power_id = factory.make_name('power_id')
212 response = self.client.post(
213 reverse('machines_handler'),
214 {
215 'architecture': make_usable_architecture(self),
216 'mac_addresses': ['aa:bb:cc:dd:ee:ff'],
217 'power_type': 'virsh',
218 'power_parameters_power_address': power_address,
219 'power_parameters_power_id': power_id,
220 })
221 parsed_result = json.loads(
222 response.content.decode(settings.DEFAULT_CHARSET))
223 machine = Machine.objects.get(system_id=parsed_result['system_id'])
224 self.assertEqual('virsh', parsed_result['power_type'])
225 self.assertEqual(
226 power_address, machine.power_parameters['power_address'])
227 self.assertEqual(power_id, machine.power_parameters['power_id'])
228
207 def test_GET_lists_machines(self):229 def test_GET_lists_machines(self):
208 # The api allows for fetching the list of Machines.230 # The api allows for fetching the list of Machines.
209 machine1 = factory.make_Node()231 machine1 = factory.make_Node()
210232
=== modified file 'src/maasserver/models/node.py'
--- src/maasserver/models/node.py 2016-10-13 00:11:49 +0000
+++ src/maasserver/models/node.py 2016-10-22 05:50:38 +0000
@@ -3084,9 +3084,12 @@
3084 for rack in self.get_boot_rack_controllers()3084 for rack in self.get_boot_rack_controllers()
3085 ]3085 ]
3086 if len(client_idents) == 0 and len(fallback_idents) == 0:3086 if len(client_idents) == 0 and len(fallback_idents) == 0:
3087 raise PowerProblem(3087 err_msg = "No rack controllers can access the BMC of node: %s" % (
3088 "No rack controllers can access the BMC of node: %s" % (3088 self.hostname)
3089 self.hostname))3089 self._register_request_event(
3090 self.owner, EVENT_TYPES.NODE_POWER_QUERY_FAILED,
3091 "Failed to query node's BMC", err_msg)
3092 raise PowerProblem(err_msg)
3090 return client_idents, fallback_idents3093 return client_idents, fallback_idents
30913094
3092 @transactional3095 @transactional
30933096
=== modified file 'src/maasserver/models/tests/test_node.py'
--- src/maasserver/models/tests/test_node.py 2016-10-18 17:35:20 +0000
+++ src/maasserver/models/tests/test_node.py 2016-10-22 05:50:38 +0000
@@ -51,7 +51,10 @@
51 POWER_STATE,51 POWER_STATE,
52 SERVICE_STATUS,52 SERVICE_STATUS,
53)53)
54from maasserver.exceptions import NodeStateViolation54from maasserver.exceptions import (
55 NodeStateViolation,
56 PowerProblem,
57)
55from maasserver.models import (58from maasserver.models import (
56 bmc as bmc_module,59 bmc as bmc_module,
57 BondInterface,60 BondInterface,
@@ -61,6 +64,7 @@
61 Controller,64 Controller,
62 Device,65 Device,
63 Domain,66 Domain,
67 EventType,
64 Fabric,68 Fabric,
65 Interface,69 Interface,
66 LicenseKey,70 LicenseKey,
@@ -5026,6 +5030,37 @@
5026 "deploy this node."], node.storage_layout_issues())5030 "deploy this node."], node.storage_layout_issues())
50275031
50285032
5033class TestGetBMCClientConnectionInfo(MAASServerTestCase):
5034
5035 def test__returns_bmc_identifiers(self):
5036 node = factory.make_Node()
5037
5038 mock_bmcs = self.patch(node.bmc, 'get_client_identifiers')
5039 fake_bmc_id = factory.make_name('system_id')
5040 mock_bmcs.return_value = [fake_bmc_id]
5041
5042 mock_fallbacks = self.patch(node, 'get_boot_rack_controllers')
5043 fake_fallback_id = factory.make_name('system_id')
5044 fallback = MagicMock()
5045 fallback.system_id = fake_fallback_id
5046 mock_fallbacks.return_value = [fallback]
5047
5048 self.assertEquals(
5049 ([fake_bmc_id], [fake_fallback_id]),
5050 node._get_bmc_client_connection_info())
5051
5052 def test__creates_event_on_error(self):
5053 node = factory.make_Node()
5054
5055 self.assertRaises(PowerProblem, node._get_bmc_client_connection_info)
5056 event_type = EventType.objects.get(
5057 name=EVENT_TYPES.NODE_POWER_QUERY_FAILED)
5058 event = node.event_set.get(type=event_type)
5059 self.assertEquals(
5060 ('No rack controllers can access the BMC of node: %s' %
5061 node.hostname), event.description)
5062
5063
5029class TestNode_Stop(MAASServerTestCase):5064class TestNode_Stop(MAASServerTestCase):
5030 """Tests for Node.stop()."""5065 """Tests for Node.stop()."""
50315066
50325067
=== modified file 'src/maasserver/websockets/handlers/machine.py'
--- src/maasserver/websockets/handlers/machine.py 2016-10-21 01:19:51 +0000
+++ src/maasserver/websockets/handlers/machine.py 2016-10-22 05:50:38 +0000
@@ -265,7 +265,10 @@
265265
266 # Start the commissioning process right away, which has the266 # Start the commissioning process right away, which has the
267 # desired side effect of initializing the node's power state.267 # desired side effect of initializing the node's power state.
268 node_obj.start_commissioning(self.user)268 d = node_obj.start_commissioning(self.user)
269 # Silently ignore errors to prevent tracebacks. The commissioning
270 # callbacks have their own logging. This fixes LP1600328.
271 d.addErrback(lambda _: None)
269272
270 return self.full_dehydrate(node_obj)273 return self.full_dehydrate(node_obj)
271274