summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuriy Zveryanskyy <yzveryanskyy@mirantis.com>2013-10-08 19:46:08 +0300
committerYuriy Zveryanskyy <yzveryanskyy@mirantis.com>2013-12-05 10:39:14 +0200
commita178be2797129784d336eb20aa3a9344ec4e5481 (patch)
tree9a6fd870eb6ac3e6428faec9078755944ffd3664
parent50dc588ce1acfc6671114f86b9fa7835a1ffa431 (diff)
downloadironic-a178be2797129784d336eb20aa3a9344ec4e5481.tar.gz
Add power control to PXE driver
Necessary node power control added to PXE driver. Power control code moved to separate module for usage in manager and drivers. Reboot handling added to power control code. Partially implements: blueprint pxe-mount-and-dd Change-Id: Ic13c046c621211d9c1533325812955233441d2da
-rw-r--r--ironic/conductor/manager.py53
-rw-r--r--ironic/conductor/utils.py89
-rw-r--r--ironic/drivers/modules/pxe.py5
-rw-r--r--ironic/tests/conductor/test_conductor_utils.py237
-rw-r--r--ironic/tests/conductor/test_manager.py195
5 files changed, 333 insertions, 246 deletions
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index 664ad4fce..5b7ae08fc 100644
--- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py
@@ -44,6 +44,7 @@ from ironic.common import exception
from ironic.common import service
from ironic.common import states
from ironic.conductor import task_manager
+from ironic.conductor import utils
from ironic.db import api as dbapi
from ironic.objects import base as objects_base
from ironic.openstack.common import excutils
@@ -172,57 +173,7 @@ class ConductorManager(service.PeriodicService):
% {'node': node_id, 'state': new_state})
with task_manager.acquire(context, node_id, shared=False) as task:
- node = task.node
- try:
- task.driver.power.validate(node)
- curr_state = task.driver.power.get_power_state(task, node)
- except Exception as e:
- with excutils.save_and_reraise_exception():
- node['last_error'] = \
- _("Failed to change power state to '%(target)s'. "
- "Error: %(error)s") % {
- 'target': new_state, 'error': e}
- node.save(context)
-
- if curr_state == new_state:
- # Neither the ironic service nor the hardware has erred. The
- # node is, for some reason, already in the requested state,
- # though we don't know why. eg, perhaps the user previously
- # requested the node POWER_ON, the network delayed those IPMI
- # packets, and they are trying again -- but the node finally
- # responds to the first request, and so the second request
- # gets to this check and stops.
- # This isn't an error, so we'll clear last_error field
- # (from previous operation), log a warning, and return.
- node['last_error'] = None
- node.save(context)
- LOG.warn(_("Not going to change_node_power_state because "
- "current state = requested state = '%(state)s'.")
- % {'state': curr_state})
- return
-
- # Set the target_power_state and clear any last_error, since we're
- # starting a new operation. This will expose to other processes
- # and clients that work is in progress.
- node['target_power_state'] = new_state
- node['last_error'] = None
- node.save(context)
-
- # take power action
- try:
- task.driver.power.set_power_state(task, node, new_state)
- except Exception as e:
- with excutils.save_and_reraise_exception():
- node['last_error'] = \
- _("Failed to change power state to '%(target)s'. "
- "Error: %(error)s") % {
- 'target': new_state, 'error': e}
- else:
- # success!
- node['power_state'] = new_state
- finally:
- node['target_power_state'] = states.NOSTATE
- node.save(context)
+ utils.node_power_action(task, task.node, new_state)
# NOTE(deva): There is a race condition in the RPC API for vendor_passthru.
# Between the validate_vendor_action and do_vendor_action calls, it's
diff --git a/ironic/conductor/utils.py b/ironic/conductor/utils.py
new file mode 100644
index 000000000..83fd7e908
--- /dev/null
+++ b/ironic/conductor/utils.py
@@ -0,0 +1,89 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# coding=utf-8
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from ironic.common import states
+from ironic.conductor import task_manager
+from ironic.openstack.common import excutils
+from ironic.openstack.common import log
+
+LOG = log.getLogger(__name__)
+
+
+@task_manager.require_exclusive_lock
+def node_power_action(task, node, state):
+ """Change power state or reset for a node.
+
+ :param task: a TaskManager instance.
+ :param node: the Node object to act upon.
+ :param state: Any power state from ironic.common.states. If the
+ state is 'REBOOT' then a reboot will be attempted, otherwise
+ the node power state is directly set to 'state'.
+ """
+ context = task.context
+ new_state = states.POWER_ON if state == states.REBOOT else state
+ try:
+ task.driver.power.validate(node)
+ if state != states.REBOOT:
+ curr_state = task.driver.power.get_power_state(task, node)
+ except Exception as e:
+ with excutils.save_and_reraise_exception():
+ node['last_error'] = \
+ _("Failed to change power state to '%(target)s'. "
+ "Error: %(error)s") % {
+ 'target': new_state, 'error': e}
+ node.save(context)
+
+ if state != states.REBOOT and curr_state == new_state:
+ # Neither the ironic service nor the hardware has erred. The
+ # node is, for some reason, already in the requested state,
+ # though we don't know why. eg, perhaps the user previously
+ # requested the node POWER_ON, the network delayed those IPMI
+ # packets, and they are trying again -- but the node finally
+ # responds to the first request, and so the second request
+ # gets to this check and stops.
+ # This isn't an error, so we'll clear last_error field
+ # (from previous operation), log a warning, and return.
+ node['last_error'] = None
+ node.save(context)
+ LOG.warn(_("Not going to change_node_power_state because "
+ "current state = requested state = '%(state)s'.")
+ % {'state': curr_state})
+ return
+
+ # Set the target_power_state and clear any last_error, since we're
+ # starting a new operation. This will expose to other processes
+ # and clients that work is in progress.
+ node['target_power_state'] = new_state
+ node['last_error'] = None
+ node.save(context)
+
+ # take power action
+ try:
+ if state != states.REBOOT:
+ task.driver.power.set_power_state(task, node, new_state)
+ else:
+ task.driver.power.reboot(task, node)
+ except Exception as e:
+ with excutils.save_and_reraise_exception():
+ node['last_error'] = \
+ _("Failed to change power state to '%(target)s'. "
+ "Error: %(error)s") % {
+ 'target': new_state, 'error': e}
+ else:
+ # success!
+ node['power_state'] = new_state
+ finally:
+ node['target_power_state'] = states.NOSTATE
+ node.save(context)
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index f1fdbaa18..cd25cda55 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -32,6 +32,7 @@ from ironic.common import keystone
from ironic.common import states
from ironic.common import utils
from ironic.conductor import task_manager
+from ironic.conductor import utils as manager_utils
from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.openstack.common import context
@@ -466,6 +467,8 @@ class PXEDeploy(base.DeployInterface):
_create_pxe_config(task, node, pxe_info)
_cache_images(node, pxe_info)
+ manager_utils.node_power_action(task, node, states.REBOOT)
+
return states.DEPLOYING
@task_manager.require_exclusive_lock
@@ -501,6 +504,8 @@ class PXEDeploy(base.DeployInterface):
_destroy_images(d_info)
+ manager_utils.node_power_action(task, node, states.POWER_OFF)
+
return states.DELETED
diff --git a/ironic/tests/conductor/test_conductor_utils.py b/ironic/tests/conductor/test_conductor_utils.py
new file mode 100644
index 000000000..735ffb4a0
--- /dev/null
+++ b/ironic/tests/conductor/test_conductor_utils.py
@@ -0,0 +1,237 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+
+from ironic.common import exception
+from ironic.common import states
+from ironic.conductor import manager
+from ironic.conductor import task_manager
+from ironic.db import api as dbapi
+from ironic.openstack.common import context
+from ironic.tests.conductor import utils as mgr_utils
+from ironic.tests.db import base
+from ironic.tests.db import utils
+
+
+class PowerActionTestCase(base.DbTestCase):
+
+ def setUp(self):
+ super(PowerActionTestCase, self).setUp()
+ self.service = manager.ConductorManager('test-host', 'test-topic')
+ self.context = context.get_admin_context()
+ self.dbapi = dbapi.get_instance()
+ self.driver = mgr_utils.get_mocked_node_manager()
+
+ def test_change_node_power_state_power_on(self):
+ """Test if change_node_power_state to turn node power on
+ is successful or not.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_OFF)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'get_power_state') \
+ as get_power_mock:
+ get_power_mock.return_value = states.POWER_OFF
+
+ self.service.change_node_power_state(self.context,
+ node, states.POWER_ON)
+ node.refresh(self.context)
+ get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_power_off(self):
+ """Test if change_node_power_state to turn node power off
+ is successful or not.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'get_power_state') \
+ as get_power_mock:
+ get_power_mock.return_value = states.POWER_ON
+
+ self.service.change_node_power_state(self.context, node,
+ states.POWER_OFF)
+ node.refresh(self.context)
+ get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
+ self.assertEqual(node['power_state'], states.POWER_OFF)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_reboot(self):
+ """Test for reboot a node."""
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'reboot') as reboot_mock:
+ self.service.change_node_power_state(self.context, node,
+ states.REBOOT)
+ node.refresh(self.context)
+ reboot_mock.assert_called_once()
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_invalid_state(self):
+ """Test if an exception is thrown when changing to an invalid
+ power state.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'get_power_state') \
+ as get_power_mock:
+ get_power_mock.return_value = states.POWER_ON
+
+ self.assertRaises(exception.InvalidParameterValue,
+ self.service.change_node_power_state,
+ self.context,
+ node,
+ "POWER")
+ node.refresh(self.context)
+ get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertNotEqual(node['last_error'], None)
+
+ # last_error is cleared when a new transaction happens
+ self.service.change_node_power_state(self.context, node,
+ states.POWER_OFF)
+ node.refresh(self.context)
+ self.assertEqual(node['power_state'], states.POWER_OFF)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_already_locked(self):
+ """Test if an exception is thrown when applying an exclusive
+ lock to the node failed.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ # check if the node is locked
+ with task_manager.acquire(self.context, node['id'], shared=False):
+ self.assertRaises(exception.NodeLocked,
+ self.service.change_node_power_state,
+ self.context,
+ node,
+ states.POWER_ON)
+ node.refresh(self.context)
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_already_being_processed(self):
+ """The target_power_state is expected to be None so it isn't
+ checked in the code. This is what happens if it is not None.
+ (Eg, if a conductor had died during a previous power-off
+ attempt and left the target_power_state set to states.POWER_OFF,
+ and the user is attempting to power-off again.)
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON,
+ target_power_state=states.POWER_OFF)
+ node = self.dbapi.create_node(ndict)
+
+ self.service.change_node_power_state(self.context, node,
+ states.POWER_OFF)
+ node.refresh(self.context)
+ self.assertEqual(node['power_state'], states.POWER_OFF)
+ self.assertEqual(node['target_power_state'], states.NOSTATE)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_in_same_state(self):
+ """Test that we don't try to set the power state if the requested
+ state is the same as the current state.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ last_error='anything but None',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'get_power_state') \
+ as get_power_mock:
+ get_power_mock.return_value = states.POWER_ON
+ with mock.patch.object(self.driver.power, 'set_power_state') \
+ as set_power_mock:
+ set_power_mock.side_effect = exception.IronicException()
+
+ self.service.change_node_power_state(self.context, node,
+ states.POWER_ON)
+ node.refresh(self.context)
+ get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
+ self.assertFalse(set_power_mock.called)
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertEqual(node['last_error'], None)
+
+ def test_change_node_power_state_invalid_driver_info(self):
+ """Test if an exception is thrown when the driver validation
+ fails.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_ON)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'validate') \
+ as validate_mock:
+ validate_mock.side_effect = exception.InvalidParameterValue(
+ 'wrong power driver info')
+
+ self.assertRaises(exception.InvalidParameterValue,
+ self.service.change_node_power_state,
+ self.context,
+ node,
+ states.POWER_ON)
+ node.refresh(self.context)
+ validate_mock.assert_called_once_with(mock.ANY)
+ self.assertEqual(node['power_state'], states.POWER_ON)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertNotEqual(node['last_error'], None)
+
+ def test_change_node_power_state_set_power_failure(self):
+ """Test if an exception is thrown when the set_power call
+ fails.
+ """
+ ndict = utils.get_test_node(driver='fake',
+ power_state=states.POWER_OFF)
+ node = self.dbapi.create_node(ndict)
+
+ with mock.patch.object(self.driver.power, 'get_power_state') \
+ as get_power_mock:
+ with mock.patch.object(self.driver.power, 'set_power_state') \
+ as set_power_mock:
+ get_power_mock.return_value = states.POWER_OFF
+ set_power_mock.side_effect = exception.IronicException()
+
+ self.assertRaises(exception.IronicException,
+ self.service.change_node_power_state,
+ self.context,
+ node,
+ states.POWER_ON)
+ node.refresh(self.context)
+ get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
+ set_power_mock.assert_called_once_with(mock.ANY, mock.ANY,
+ states.POWER_ON)
+ self.assertEqual(node['power_state'], states.POWER_OFF)
+ self.assertEqual(node['target_power_state'], None)
+ self.assertNotEqual(node['last_error'], None)
diff --git a/ironic/tests/conductor/test_manager.py b/ironic/tests/conductor/test_manager.py
index e168f9be2..acf477d35 100644
--- a/ironic/tests/conductor/test_manager.py
+++ b/ironic/tests/conductor/test_manager.py
@@ -180,201 +180,6 @@ class ManagerTestCase(base.DbTestCase):
res = objects.Node.get_by_uuid(self.context, node['uuid'])
self.assertEqual(res['driver'], existing_driver)
- def test_change_node_power_state_power_on(self):
- """Test if change_node_power_state to turn node power on
- is successful or not.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_OFF)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'get_power_state') \
- as get_power_mock:
- get_power_mock.return_value = states.POWER_OFF
-
- self.service.change_node_power_state(self.context,
- node, states.POWER_ON)
- node.refresh()
- get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
- self.assertEqual(node['power_state'], states.POWER_ON)
- self.assertEqual(node['target_power_state'], None)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_power_off(self):
- """Test if change_node_power_state to turn node power off
- is successful or not.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_ON)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'get_power_state') \
- as get_power_mock:
- get_power_mock.return_value = states.POWER_ON
-
- self.service.change_node_power_state(self.context, node,
- states.POWER_OFF)
- node.refresh()
- get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
- self.assertEqual(node['power_state'], states.POWER_OFF)
- self.assertEqual(node['target_power_state'], None)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_to_invalid_state(self):
- """Test if an exception is thrown when changing to an invalid
- power state.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_ON)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'get_power_state') \
- as get_power_mock:
- get_power_mock.return_value = states.POWER_ON
-
- self.assertRaises(exception.InvalidParameterValue,
- self.service.change_node_power_state,
- self.context,
- node,
- "POWER")
- node.refresh()
- get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
- self.assertEqual(node['power_state'], states.POWER_ON)
- self.assertEqual(node['target_power_state'], None)
- self.assertNotEqual(node['last_error'], None)
-
- # last_error is cleared when a new transaction happens
- self.service.change_node_power_state(self.context, node,
- states.POWER_OFF)
- node.refresh()
- self.assertEqual(node['power_state'], states.POWER_OFF)
- self.assertEqual(node['target_power_state'], None)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_already_locked(self):
- """Test if an exception is thrown when applying an exclusive
- lock to the node failed.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_ON)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- # check if the node is locked
- with task_manager.acquire(self.context, node['id'], shared=False):
- self.assertRaises(exception.NodeLocked,
- self.service.change_node_power_state,
- self.context,
- node,
- states.POWER_ON)
- node.refresh()
- self.assertEqual(node['power_state'], states.POWER_ON)
- self.assertEqual(node['target_power_state'], None)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_already_being_processed(self):
- """The target_power_state is expected to be None so it isn't
- checked in the code. This is what happens if it is not None.
- (Eg, if a conductor had died during a previous power-off
- attempt and left the target_power_state set to states.POWER_OFF,
- and the user is attempting to power-off again.)
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_ON,
- target_power_state=states.POWER_OFF)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- self.service.change_node_power_state(self.context, node,
- states.POWER_OFF)
- node.refresh()
- self.assertEqual(node['power_state'], states.POWER_OFF)
- self.assertEqual(node['target_power_state'], states.NOSTATE)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_in_same_state(self):
- """Test that we don't try to set the power state if the requested
- state is the same as the current state.
- """
- ndict = utils.get_test_node(driver='fake',
- last_error='anything but None',
- power_state=states.POWER_ON)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'get_power_state') \
- as get_power_mock:
- get_power_mock.return_value = states.POWER_ON
- with mock.patch.object(self.driver.power, 'set_power_state') \
- as set_power_mock:
- set_power_mock.side_effect = exception.IronicException()
-
- self.service.change_node_power_state(self.context, node,
- states.POWER_ON)
- node.refresh()
- get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
- self.assertFalse(set_power_mock.called)
- self.assertEqual(node['power_state'], states.POWER_ON)
- self.assertEqual(node['target_power_state'], None)
- self.assertEqual(node['last_error'], None)
-
- def test_change_node_power_state_invalid_driver_info(self):
- """Test if an exception is thrown when the driver validation
- fails.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_ON)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'validate') \
- as validate_mock:
- validate_mock.side_effect = exception.InvalidParameterValue(
- 'wrong power driver info')
-
- self.assertRaises(exception.InvalidParameterValue,
- self.service.change_node_power_state,
- self.context,
- node,
- states.POWER_ON)
- node.refresh()
- validate_mock.assert_called_once_with(mock.ANY)
- self.assertEqual(node['power_state'], states.POWER_ON)
- self.assertEqual(node['target_power_state'], None)
- self.assertNotEqual(node['last_error'], None)
-
- def test_change_node_power_state_set_power_failure(self):
- """Test if an exception is thrown when the set_power call
- fails.
- """
- ndict = utils.get_test_node(driver='fake',
- power_state=states.POWER_OFF)
- node = self.dbapi.create_node(ndict)
- node = objects.Node.get_by_uuid(self.context, node['uuid'])
-
- with mock.patch.object(self.driver.power, 'get_power_state') \
- as get_power_mock:
- with mock.patch.object(self.driver.power, 'set_power_state') \
- as set_power_mock:
- get_power_mock.return_value = states.POWER_OFF
- set_power_mock.side_effect = exception.IronicException()
-
- self.assertRaises(exception.IronicException,
- self.service.change_node_power_state,
- self.context,
- node,
- states.POWER_ON)
- node.refresh()
- get_power_mock.assert_called_once_with(mock.ANY, mock.ANY)
- set_power_mock.assert_called_once_with(mock.ANY, mock.ANY,
- states.POWER_ON)
- self.assertEqual(node['power_state'], states.POWER_OFF)
- self.assertEqual(node['target_power_state'], None)
- self.assertNotEqual(node['last_error'], None)
-
def test_vendor_action(self):
n = utils.get_test_node(driver='fake')
self.dbapi.create_node(n)