DRAC: switch to python-dracclient on power interface
DRAC specific code from Ironic is moving to its own project, to python-dracclient project. This patch starts using the new library. Change-Id: I68251fc2b5dd169bb22bbbe38fe9eea16c5ac806 Partial-Bug: #1454492 Depends-On: Iab9d9f7e4e25e3d3fdec9b28fe49a7226e68c9ff
This commit is contained in:
parent
ba7ec49f88
commit
0ced09b832
@ -12,6 +12,7 @@ python-oneviewclient<2.1.0,>=2.0.2
|
||||
python-scciclient>=0.3.0
|
||||
python-seamicroclient>=0.4.0
|
||||
UcsSdk==0.8.2.2
|
||||
python-dracclient>=0.0.5
|
||||
|
||||
# The drac and amt driver import a python module called "pywsman", however,
|
||||
# this does not exist on pypi.
|
||||
|
@ -484,6 +484,10 @@ class IloOperationNotSupported(IronicException):
|
||||
_msg_fmt = _("%(operation)s not supported. error: %(error)s")
|
||||
|
||||
|
||||
class DracOperationError(IronicException):
|
||||
_msg_fmt = _('DRAC operation failed. Reason: %(error)s')
|
||||
|
||||
|
||||
class DracRequestFailed(IronicException):
|
||||
pass
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
DRAC Driver for remote system management using Dell Remote Access Card.
|
||||
"""
|
||||
@ -37,6 +38,11 @@ class PXEDracDriver(base.BaseDriver):
|
||||
driver=self.__class__.__name__,
|
||||
reason=_('Unable to import pywsman library'))
|
||||
|
||||
if not importutils.try_import('dracclient'):
|
||||
raise exception.DriverLoadError(
|
||||
driver=self.__class__.__name__,
|
||||
reason=_('Unable to import python-dracclient library'))
|
||||
|
||||
self.power = power.DracPower()
|
||||
self.boot = pxe.PXEBoot()
|
||||
self.deploy = iscsi_deploy.ISCSIDeploy()
|
||||
|
@ -182,6 +182,11 @@ class FakeDracDriver(base.BaseDriver):
|
||||
driver=self.__class__.__name__,
|
||||
reason=_('Unable to import pywsman library'))
|
||||
|
||||
if not importutils.try_import('dracclient'):
|
||||
raise exception.DriverLoadError(
|
||||
driver=self.__class__.__name__,
|
||||
reason=_('Unable to import python-dracclient library'))
|
||||
|
||||
self.power = drac_power.DracPower()
|
||||
self.deploy = fake.FakeDeploy()
|
||||
self.management = drac_mgmt.DracManagement()
|
||||
|
@ -22,6 +22,9 @@ from ironic.common.i18n import _
|
||||
from ironic.common import utils
|
||||
|
||||
pywsman = importutils.try_import('pywsman')
|
||||
drac_client = importutils.try_import('dracclient.client')
|
||||
drac_constants = importutils.try_import('dracclient.constants')
|
||||
|
||||
|
||||
REQUIRED_PROPERTIES = {
|
||||
'drac_host': _('IP address or hostname of the DRAC card. Required.'),
|
||||
@ -74,6 +77,10 @@ def parse_driver_info(node):
|
||||
try:
|
||||
parsed_driver_info['drac_protocol'] = str(
|
||||
driver_info.get('drac_protocol', 'https'))
|
||||
|
||||
if parsed_driver_info['drac_protocol'] not in ['http', 'https']:
|
||||
error_msgs.append(_("'drac_protocol' must be either 'http' or "
|
||||
"'https'."))
|
||||
except UnicodeEncodeError:
|
||||
error_msgs.append(_("'drac_protocol' contains non-ASCII symbol."))
|
||||
|
||||
@ -89,6 +96,25 @@ def parse_driver_info(node):
|
||||
return parsed_driver_info
|
||||
|
||||
|
||||
def get_drac_client(node):
|
||||
"""Returns a DRACClient object from python-dracclient library.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:returns: a DRACClient object.
|
||||
:raises: InvalidParameterValue if mandatory information is missing on the
|
||||
node or on invalid input.
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
client = drac_client.DRACClient(driver_info['drac_host'],
|
||||
driver_info['drac_username'],
|
||||
driver_info['drac_password'],
|
||||
driver_info['drac_port'],
|
||||
driver_info['drac_path'],
|
||||
driver_info['drac_protocol'])
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def find_xml(doc, item, namespace, find_all=False):
|
||||
"""Find the first or all elements in an ElementTree object.
|
||||
|
||||
|
@ -12,11 +12,10 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
DRAC Power Driver using the Base Server Profile
|
||||
DRAC power interface
|
||||
"""
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
@ -24,80 +23,74 @@ from ironic.common.i18n import _LE
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules.drac import client as drac_client
|
||||
from ironic.drivers.modules.drac import common as drac_common
|
||||
from ironic.drivers.modules.drac import resource_uris
|
||||
|
||||
pywsman = importutils.try_import('pywsman')
|
||||
drac_constants = importutils.try_import('dracclient.constants')
|
||||
drac_exceptions = importutils.try_import('dracclient.exceptions')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
POWER_STATES = {
|
||||
'2': states.POWER_ON,
|
||||
'3': states.POWER_OFF,
|
||||
'11': states.REBOOT,
|
||||
}
|
||||
if drac_constants:
|
||||
POWER_STATES = {
|
||||
drac_constants.POWER_ON: states.POWER_ON,
|
||||
drac_constants.POWER_OFF: states.POWER_OFF,
|
||||
drac_constants.REBOOT: states.REBOOT
|
||||
}
|
||||
|
||||
REVERSE_POWER_STATES = dict((v, k) for (k, v) in POWER_STATES.items())
|
||||
REVERSE_POWER_STATES = dict((v, k) for (k, v) in POWER_STATES.items())
|
||||
|
||||
|
||||
def _get_power_state(node):
|
||||
"""Returns the current power state of the node
|
||||
"""Returns the current power state of the node.
|
||||
|
||||
:param node: The node.
|
||||
:returns: power state, one of :mod: `ironic.common.states`.
|
||||
:raises: DracClientError if the client received unexpected response.
|
||||
:param node: an ironic node object.
|
||||
:returns: the power state, one of :mod:`ironic.common.states`.
|
||||
:raises: InvalidParameterValue if required DRAC credentials are missing.
|
||||
:raises: DracOperationError on an error from python-dracclient
|
||||
"""
|
||||
|
||||
client = drac_client.get_wsman_client(node)
|
||||
filter_query = ('select EnabledState,ElementName from DCIM_ComputerSystem '
|
||||
'where Name="srv:system"')
|
||||
client = drac_common.get_drac_client(node)
|
||||
|
||||
try:
|
||||
doc = client.wsman_enumerate(resource_uris.DCIM_ComputerSystem,
|
||||
filter_query=filter_query)
|
||||
except exception.DracClientError as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('DRAC driver failed to get power state for node '
|
||||
'%(node_uuid)s. Reason: %(error)s.'),
|
||||
{'node_uuid': node.uuid, 'error': exc})
|
||||
drac_power_state = client.get_power_state()
|
||||
except drac_exceptions.BaseClientException as exc:
|
||||
LOG.error(_LE('DRAC driver failed to get power state for node '
|
||||
'%(node_uuid)s. Reason: %(error)s.'),
|
||||
{'node_uuid': node.uuid, 'error': exc})
|
||||
raise exception.DracOperationError(error=exc)
|
||||
|
||||
enabled_state = drac_common.find_xml(doc, 'EnabledState',
|
||||
resource_uris.DCIM_ComputerSystem)
|
||||
return POWER_STATES[enabled_state.text]
|
||||
return POWER_STATES[drac_power_state]
|
||||
|
||||
|
||||
def _set_power_state(node, target_state):
|
||||
def _set_power_state(node, power_state):
|
||||
"""Turns the server power on/off or do a reboot.
|
||||
|
||||
:param node: an ironic node object.
|
||||
:param target_state: target state of the node.
|
||||
:raises: DracClientError if the client received unexpected response.
|
||||
:raises: InvalidParameterValue if an invalid power state was specified.
|
||||
:param power_state: a power state from :mod:`ironic.common.states`.
|
||||
:raises: InvalidParameterValue if required DRAC credentials are missing.
|
||||
:raises: DracOperationError on an error from python-dracclient
|
||||
"""
|
||||
|
||||
client = drac_client.get_wsman_client(node)
|
||||
selectors = {'CreationClassName': 'DCIM_ComputerSystem',
|
||||
'Name': 'srv:system'}
|
||||
properties = {'RequestedState': REVERSE_POWER_STATES[target_state]}
|
||||
client = drac_common.get_drac_client(node)
|
||||
target_power_state = REVERSE_POWER_STATES[power_state]
|
||||
|
||||
try:
|
||||
client.wsman_invoke(resource_uris.DCIM_ComputerSystem,
|
||||
'RequestStateChange', selectors, properties)
|
||||
except exception.DracRequestFailed as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('DRAC driver failed to set power state for node '
|
||||
'%(node_uuid)s to %(target_power_state)s. '
|
||||
'Reason: %(error)s.'),
|
||||
{'node_uuid': node.uuid,
|
||||
'target_power_state': target_state,
|
||||
'error': exc})
|
||||
client.set_power_state(target_power_state)
|
||||
except drac_exceptions.BaseClientException as exc:
|
||||
LOG.error(_LE('DRAC driver failed to set power state for node '
|
||||
'%(node_uuid)s to %(power_state)s. '
|
||||
'Reason: %(error)s.'),
|
||||
{'node_uuid': node.uuid,
|
||||
'power_state': power_state,
|
||||
'error': exc})
|
||||
raise exception.DracOperationError(error=exc)
|
||||
|
||||
|
||||
class DracPower(base.PowerInterface):
|
||||
"""Interface for power-related actions."""
|
||||
|
||||
def get_properties(self):
|
||||
"""Return the properties of the interface."""
|
||||
return drac_common.COMMON_PROPERTIES
|
||||
|
||||
def validate(self, task):
|
||||
@ -114,39 +107,36 @@ class DracPower(base.PowerInterface):
|
||||
return drac_common.parse_driver_info(task.node)
|
||||
|
||||
def get_power_state(self, task):
|
||||
"""Return the power state of the task's node.
|
||||
"""Return the power state of the node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:returns: a power state. One of :mod:`ironic.common.states`.
|
||||
:raises: DracClientError if the client received unexpected response.
|
||||
:returns: the power state, one of :mod:`ironic.common.states`.
|
||||
:raises: InvalidParameterValue if required DRAC credentials are
|
||||
missing.
|
||||
:raises: DracOperationError on an error from python-dracclient.
|
||||
"""
|
||||
return _get_power_state(task.node)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def set_power_state(self, task, power_state):
|
||||
"""Set the power state of the task's node.
|
||||
"""Set the power state of the node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param power_state: Any power state from :mod:`ironic.common.states`.
|
||||
:raises: DracClientError if the client received unexpected response.
|
||||
:raises: DracOperationFailed if the client received response with an
|
||||
error message.
|
||||
:raises: DracUnexpectedReturnValue if the client received a response
|
||||
with unexpected return value.
|
||||
|
||||
:param power_state: a power state from :mod:`ironic.common.states`.
|
||||
:raises: InvalidParameterValue if required DRAC credentials are
|
||||
missing.
|
||||
:raises: DracOperationError on an error from python-dracclient.
|
||||
"""
|
||||
_set_power_state(task.node, power_state)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
def reboot(self, task):
|
||||
"""Perform a hard reboot of the task's node.
|
||||
"""Perform a reboot of the task's node.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: DracClientError if the client received unexpected response.
|
||||
:raises: DracOperationFailed if the client received response with an
|
||||
error message.
|
||||
:raises: DracUnexpectedReturnValue if the client received a response
|
||||
with unexpected return value.
|
||||
:raises: InvalidParameterValue if required DRAC credentials are
|
||||
missing.
|
||||
:raises: DracOperationError on an error from python-dracclient.
|
||||
"""
|
||||
|
||||
current_power_state = _get_power_state(task.node)
|
||||
|
@ -17,6 +17,8 @@ Test class for common methods used by DRAC modules.
|
||||
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import dracclient.client
|
||||
import mock
|
||||
from testtools.matchers import HasLength
|
||||
|
||||
from ironic.common import exception
|
||||
@ -86,6 +88,15 @@ class DracCommonMethodsTestCase(db_base.DbTestCase):
|
||||
info = drac_common.parse_driver_info(node)
|
||||
self.assertEqual('https', info.get('drac_protocol'))
|
||||
|
||||
def test_parse_driver_info_invalid_protocol(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_drac',
|
||||
driver_info=INFO_DICT)
|
||||
node.driver_info['drac_protocol'] = 'foo'
|
||||
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
drac_common.parse_driver_info, node)
|
||||
|
||||
def test_parse_driver_info_missing_username(self):
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_drac',
|
||||
@ -102,6 +113,18 @@ class DracCommonMethodsTestCase(db_base.DbTestCase):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
drac_common.parse_driver_info, node)
|
||||
|
||||
@mock.patch.object(dracclient.client, 'DRACClient', autospec=True)
|
||||
def test_get_drac_client(self, mock_dracclient):
|
||||
expected_call = mock.call('1.2.3.4', 'admin', 'fake', 443, '/wsman',
|
||||
'https')
|
||||
node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_drac',
|
||||
driver_info=INFO_DICT)
|
||||
|
||||
drac_common.get_drac_client(node)
|
||||
|
||||
self.assertEqual(mock_dracclient.mock_calls, [expected_call])
|
||||
|
||||
def test_find_xml(self):
|
||||
namespace = 'http://fake'
|
||||
value = 'fake_value'
|
||||
|
@ -12,164 +12,107 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Test class for DRAC Power Driver
|
||||
Test class for DRAC power interface
|
||||
"""
|
||||
|
||||
from dracclient import constants as drac_constants
|
||||
from dracclient import exceptions as drac_exceptions
|
||||
import mock
|
||||
|
||||
from ironic.common import exception
|
||||
from ironic.common import states
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules.drac import client as drac_client
|
||||
from ironic.drivers.modules.drac import common as drac_common
|
||||
from ironic.drivers.modules.drac import power as drac_power
|
||||
from ironic.drivers.modules.drac import resource_uris
|
||||
from ironic.tests.unit.conductor import mgr_utils
|
||||
from ironic.tests.unit.db import base
|
||||
from ironic.tests.unit.db import utils as db_utils
|
||||
from ironic.tests.unit.drivers.modules.drac import utils as test_utils
|
||||
from ironic.tests.unit.drivers import third_party_driver_mock_specs \
|
||||
as mock_specs
|
||||
from ironic.tests.unit.objects import utils as obj_utils
|
||||
|
||||
INFO_DICT = db_utils.get_test_drac_info()
|
||||
|
||||
|
||||
@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
|
||||
@mock.patch.object(drac_power, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
|
||||
class DracPowerInternalMethodsTestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DracPowerInternalMethodsTestCase, self).setUp()
|
||||
driver_info = INFO_DICT
|
||||
self.node = db_utils.create_test_node(
|
||||
driver='fake_drac',
|
||||
driver_info=driver_info,
|
||||
instance_uuid='instance_uuid_123')
|
||||
|
||||
def test__get_power_state(self, mock_power_pywsman, mock_client_pywsman):
|
||||
result_xml = test_utils.build_soap_xml(
|
||||
[{'EnabledState': '2'}], resource_uris.DCIM_ComputerSystem)
|
||||
mock_xml = test_utils.mock_wsman_root(result_xml)
|
||||
mock_pywsman_client = mock_client_pywsman.Client.return_value
|
||||
mock_pywsman_client.enumerate.return_value = mock_xml
|
||||
|
||||
self.assertEqual(states.POWER_ON,
|
||||
drac_power._get_power_state(self.node))
|
||||
|
||||
mock_pywsman_client.enumerate.assert_called_once_with(
|
||||
mock.ANY, mock.ANY, resource_uris.DCIM_ComputerSystem)
|
||||
|
||||
def test__set_power_state(self, mock_power_pywsman, mock_client_pywsman):
|
||||
result_xml = test_utils.build_soap_xml(
|
||||
[{'ReturnValue': drac_client.RET_SUCCESS}],
|
||||
resource_uris.DCIM_ComputerSystem)
|
||||
mock_xml = test_utils.mock_wsman_root(result_xml)
|
||||
mock_pywsman_client = mock_client_pywsman.Client.return_value
|
||||
mock_pywsman_client.invoke.return_value = mock_xml
|
||||
|
||||
mock_pywsman_clientopts = (
|
||||
mock_client_pywsman.ClientOptions.return_value)
|
||||
|
||||
drac_power._set_power_state(self.node, states.POWER_ON)
|
||||
|
||||
mock_pywsman_clientopts.add_selector.assert_has_calls([
|
||||
mock.call('CreationClassName', 'DCIM_ComputerSystem'),
|
||||
mock.call('Name', 'srv:system')
|
||||
], any_order=True)
|
||||
mock_pywsman_clientopts.add_property.assert_called_once_with(
|
||||
'RequestedState', '2')
|
||||
|
||||
mock_pywsman_client.invoke.assert_called_once_with(
|
||||
mock.ANY, resource_uris.DCIM_ComputerSystem,
|
||||
'RequestStateChange', None)
|
||||
|
||||
def test__set_power_state_fail(self, mock_power_pywsman,
|
||||
mock_client_pywsman):
|
||||
result_xml = test_utils.build_soap_xml(
|
||||
[{'ReturnValue': drac_client.RET_ERROR,
|
||||
'Message': 'error message'}],
|
||||
resource_uris.DCIM_ComputerSystem)
|
||||
|
||||
mock_xml = test_utils.mock_wsman_root(result_xml)
|
||||
mock_pywsman_client = mock_client_pywsman.Client.return_value
|
||||
mock_pywsman_client.invoke.return_value = mock_xml
|
||||
|
||||
mock_pywsman_clientopts = (
|
||||
mock_client_pywsman.ClientOptions.return_value)
|
||||
|
||||
self.assertRaises(exception.DracOperationFailed,
|
||||
drac_power._set_power_state, self.node,
|
||||
states.POWER_ON)
|
||||
|
||||
mock_pywsman_clientopts.add_selector.assert_has_calls([
|
||||
mock.call('CreationClassName', 'DCIM_ComputerSystem'),
|
||||
mock.call('Name', 'srv:system')
|
||||
], any_order=True)
|
||||
mock_pywsman_clientopts.add_property.assert_called_once_with(
|
||||
'RequestedState', '2')
|
||||
|
||||
mock_pywsman_client.invoke.assert_called_once_with(
|
||||
mock.ANY, resource_uris.DCIM_ComputerSystem,
|
||||
'RequestStateChange', None)
|
||||
|
||||
|
||||
@mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
|
||||
autospec=True)
|
||||
class DracPowerTestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DracPowerTestCase, self).setUp()
|
||||
driver_info = INFO_DICT
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_drac")
|
||||
self.node = db_utils.create_test_node(
|
||||
driver='fake_drac',
|
||||
driver_info=driver_info,
|
||||
instance_uuid='instance_uuid_123')
|
||||
mgr_utils.mock_the_extension_manager(driver='fake_drac')
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='fake_drac',
|
||||
driver_info=INFO_DICT)
|
||||
|
||||
def test_get_properties(self):
|
||||
def test_get_properties(self, mock_get_drac_client):
|
||||
expected = drac_common.COMMON_PROPERTIES
|
||||
driver = drac_power.DracPower()
|
||||
self.assertEqual(expected, driver.get_properties())
|
||||
|
||||
@mock.patch.object(drac_power, '_get_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
def test_get_power_state(self, mock_get_power_state):
|
||||
mock_get_power_state.return_value = states.POWER_ON
|
||||
driver = drac_power.DracPower()
|
||||
task = mock.Mock()
|
||||
task.node.return_value = self.node
|
||||
def test_get_power_state(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
mock_client.get_power_state.return_value = drac_constants.POWER_ON
|
||||
|
||||
self.assertEqual(states.POWER_ON, driver.get_power_state(task))
|
||||
mock_get_power_state.assert_called_once_with(task.node)
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
power_state = task.driver.power.get_power_state(task)
|
||||
|
||||
self.assertEqual(states.POWER_ON, power_state)
|
||||
mock_client.get_power_state.assert_called_once_with()
|
||||
|
||||
def test_get_power_state_fail(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
exc = drac_exceptions.BaseClientException('boom')
|
||||
mock_client.get_power_state.side_effect = exc
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
self.assertRaises(exception.DracOperationError,
|
||||
task.driver.power.get_power_state, task)
|
||||
|
||||
mock_client.get_power_state.assert_called_once_with()
|
||||
|
||||
def test_set_power_state(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
|
||||
@mock.patch.object(drac_power, '_set_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
def test_set_power_state(self, mock_set_power_state):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.power.set_power_state(task, states.POWER_ON)
|
||||
mock_set_power_state.assert_called_once_with(task.node,
|
||||
states.POWER_ON)
|
||||
task.driver.power.set_power_state(task, states.POWER_OFF)
|
||||
|
||||
drac_power_state = drac_power.REVERSE_POWER_STATES[states.POWER_OFF]
|
||||
mock_client.set_power_state.assert_called_once_with(drac_power_state)
|
||||
|
||||
def test_set_power_state_fail(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
exc = drac_exceptions.BaseClientException('boom')
|
||||
mock_client.set_power_state.side_effect = exc
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
self.assertRaises(exception.DracOperationError,
|
||||
task.driver.power.set_power_state, task,
|
||||
states.POWER_OFF)
|
||||
|
||||
drac_power_state = drac_power.REVERSE_POWER_STATES[states.POWER_OFF]
|
||||
mock_client.set_power_state.assert_called_once_with(drac_power_state)
|
||||
|
||||
def test_reboot_while_powered_on(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
mock_client.get_power_state.return_value = drac_constants.POWER_ON
|
||||
|
||||
@mock.patch.object(drac_power, '_set_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_power, '_get_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
def test_reboot(self, mock_get_power_state, mock_set_power_state):
|
||||
mock_get_power_state.return_value = states.POWER_ON
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.power.reboot(task)
|
||||
mock_set_power_state.assert_called_once_with(task.node,
|
||||
states.REBOOT)
|
||||
|
||||
@mock.patch.object(drac_power, '_set_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(drac_power, '_get_power_state', spec_set=True,
|
||||
autospec=True)
|
||||
def test_reboot_in_power_off(self, mock_get_power_state,
|
||||
mock_set_power_state):
|
||||
mock_get_power_state.return_value = states.POWER_OFF
|
||||
drac_power_state = drac_power.REVERSE_POWER_STATES[states.REBOOT]
|
||||
mock_client.set_power_state.assert_called_once_with(drac_power_state)
|
||||
|
||||
def test_reboot_while_powered_off(self, mock_get_drac_client):
|
||||
mock_client = mock_get_drac_client.return_value
|
||||
mock_client.get_power_state.return_value = drac_constants.POWER_OFF
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.power.reboot(task)
|
||||
mock_set_power_state.assert_called_once_with(task.node,
|
||||
states.POWER_ON)
|
||||
|
||||
drac_power_state = drac_power.REVERSE_POWER_STATES[states.POWER_ON]
|
||||
mock_client.set_power_state.assert_called_once_with(drac_power_state)
|
||||
|
@ -16,6 +16,23 @@
|
||||
"""This module provides mock 'specs' for third party modules that can be used
|
||||
when needing to mock those third party modules"""
|
||||
|
||||
# python-dracclient
|
||||
DRACCLIENT_SPEC = (
|
||||
'client',
|
||||
'constants',
|
||||
'exceptions'
|
||||
)
|
||||
|
||||
DRACCLIENT_CLIENT_MOD_SPEC = (
|
||||
'DRACClient',
|
||||
)
|
||||
|
||||
DRACCLIENT_CONSTANTS_MOD_SPEC = (
|
||||
'POWER_OFF',
|
||||
'POWER_ON',
|
||||
'REBOOT'
|
||||
)
|
||||
|
||||
# iboot
|
||||
IBOOT_SPEC = (
|
||||
'iBootInterface',
|
||||
|
@ -28,6 +28,8 @@ Current list of mocked libraries:
|
||||
- pysnmp
|
||||
- scciclient
|
||||
- oneview_client
|
||||
- pywsman
|
||||
- python-dracclient
|
||||
"""
|
||||
|
||||
import sys
|
||||
@ -137,6 +139,29 @@ if not pywsman:
|
||||
if 'ironic.drivers.modules.amt' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.amt'])
|
||||
|
||||
# attempt to load the external 'python-dracclient' library, which is required
|
||||
# by the optional drivers.modules.drac module. 'python-dracclient' is going to
|
||||
# be used in the DRAC driver, once we will complete migration from 'pywsman'
|
||||
dracclient = importutils.try_import('dracclient')
|
||||
if not dracclient:
|
||||
dracclient = mock.MagicMock(spec_set=mock_specs.DRACCLIENT_SPEC)
|
||||
dracclient.client = mock.MagicMock(
|
||||
spec_set=mock_specs.DRACCLIENT_CLIENT_MOD_SPEC)
|
||||
dracclient.constants = mock.MagicMock(
|
||||
spec_set=mock_specs.DRACCLIENT_CONSTANTS_MOD_SPEC,
|
||||
POWER_OFF=mock.sentinel.POWER_OFF,
|
||||
POWER_ON=mock.sentinel.POWER_ON,
|
||||
REBOOT=mock.sentinel.REBOOT)
|
||||
sys.modules['dracclient'] = dracclient
|
||||
sys.modules['dracclient.client'] = dracclient.client
|
||||
sys.modules['dracclient.constants'] = dracclient.constants
|
||||
sys.modules['dracclient.exceptions'] = dracclient.exceptions
|
||||
dracclient.exceptions.BaseClientException = type('BaseClientException',
|
||||
(Exception,), {})
|
||||
# Now that the external library has been mocked, if anything had already
|
||||
# loaded any of the drivers, reload them.
|
||||
if 'ironic.drivers.modules.drac' in sys.modules:
|
||||
six.moves.reload_module(sys.modules['ironic.drivers.modules.drac'])
|
||||
|
||||
# attempt to load the external 'iboot' library, which is required by
|
||||
# the optional drivers.modules.iboot module
|
||||
|
Loading…
Reference in New Issue
Block a user