def destroy_node(self, context, node_id): """Delete a node. :param context: request context. :param node_id: node id or uuid. :raises: NodeLocked if node is locked by another conductor. :raises: NodeAssociated if the node contains an instance associated with it. :raises: NodeInWrongPowerState if the node is not powered off. """ with task_manager.acquire(context, node_id) as task: node = task.node if node.instance_uuid is not None: raise exception.NodeAssociated(node=node.uuid, instance=node.instance_uuid) if node.power_state not in [states.POWER_OFF, states.NOSTATE]: msg = (_("Node %s can't be deleted because it's not " "powered off") % node.uuid) raise exception.NodeInWrongPowerState(msg) # FIXME(comstud): Remove context argument after we ensure # every instantiation of Node includes the context node.destroy(context) LOG.info(_LI('Successfully deleted node %(node)s.'), {'node': node.uuid})
def test_update_fails_bad_state(self): fake_err = 'Fake Power State' rpcapi.ConductorAPI.update_node(mox.IgnoreArg(), mox.IgnoreArg()).\ AndRaise(exception.NodeInWrongPowerState( node=self.node['uuid'], pstate=fake_err)) self.mox.ReplayAll() response = self.patch_json('/nodes/%s' % self.node['uuid'], {'instance_uuid': 'fake instance uuid'}, expect_errors=True) self.assertEqual(response.content_type, 'application/json') # TODO(deva): change to 409 when wsme 0.5b3 released self.assertEqual(response.status_code, 400) self.mox.VerifyAll()
def test_update_fails_bad_state(self): fake_err = 'Fake Power State' self.mock_update_node.side_effect = exception.NodeInWrongPowerState( node=self.node['uuid'], pstate=fake_err) response = self.patch_json('/nodes/%s' % self.node['uuid'], [{'path': '/instance_uuid', 'value': 'aaaaaaaa-1111-bbbb-2222-cccccccccccc', 'op': 'replace'}], expect_errors=True) self.assertEqual('application/json', response.content_type) self.assertEqual(409, response.status_code) self.mock_update_node.assert_called_once_with( mock.ANY, mock.ANY, 'test-topic')
def update_node(self, context, node_obj): """Update a node with the supplied data. This method is the main "hub" for PUT and PATCH requests in the API. It ensures that the requested change is safe to perform, validates the parameters with the node's driver, if necessary. :param context: an admin context :param node_obj: a changed (but not saved) node object. """ node_id = node_obj.uuid LOG.debug(_("RPC update_node called for node %s.") % node_id) delta = node_obj.obj_what_changed() if 'power_state' in delta: raise exception.IronicException( _("Invalid method call: update_node can not change node state." )) driver_name = node_obj.get('driver') if 'driver' in delta else None with task_manager.acquire(context, node_id, shared=False, driver_name=driver_name) as task: # TODO(deva): Determine what value will be passed by API when # instance_uuid needs to be unset, and handle it. if 'instance_uuid' in delta: task.driver.power.validate(task, node_obj) node_obj['power_state'] = \ task.driver.power.get_power_state(task, node_obj) if node_obj['power_state'] != states.POWER_OFF: raise exception.NodeInWrongPowerState( node=node_id, pstate=node_obj['power_state']) # update any remaining parameters, then save node_obj.save(context) return node_obj
def destroy_node(self, context, node_id): """Delete a node. :param context: request context. :param node_id: node id or uuid. :raises: NodeLocked if node is locked by another conductor. :raises: NodeAssociated if the node contains an instance associated with it. :raises: NodeInWrongPowerState if the node is not powered off. """ with task_manager.acquire(context, node_id) as task: node = task.node if node.instance_uuid is not None: raise exception.NodeAssociated(node=node.uuid, instance=node.instance_uuid) if node.power_state not in [states.POWER_OFF, states.NOSTATE]: msg = (_("Node %s can't be deleted because it's not " "powered off") % node.uuid) raise exception.NodeInWrongPowerState(msg) self.dbapi.destroy_node(node_id)