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:
Imre Farkas 2015-10-22 15:01:17 +02:00
parent ba7ec49f88
commit 0ced09b832
10 changed files with 227 additions and 187 deletions

View File

@ -12,6 +12,7 @@ python-oneviewclient<2.1.0,>=2.0.2
python-scciclient>=0.3.0 python-scciclient>=0.3.0
python-seamicroclient>=0.4.0 python-seamicroclient>=0.4.0
UcsSdk==0.8.2.2 UcsSdk==0.8.2.2
python-dracclient>=0.0.5
# The drac and amt driver import a python module called "pywsman", however, # The drac and amt driver import a python module called "pywsman", however,
# this does not exist on pypi. # this does not exist on pypi.

View File

@ -484,6 +484,10 @@ class IloOperationNotSupported(IronicException):
_msg_fmt = _("%(operation)s not supported. error: %(error)s") _msg_fmt = _("%(operation)s not supported. error: %(error)s")
class DracOperationError(IronicException):
_msg_fmt = _('DRAC operation failed. Reason: %(error)s')
class DracRequestFailed(IronicException): class DracRequestFailed(IronicException):
pass pass

View File

@ -10,6 +10,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
""" """
DRAC Driver for remote system management using Dell Remote Access Card. DRAC Driver for remote system management using Dell Remote Access Card.
""" """
@ -37,6 +38,11 @@ class PXEDracDriver(base.BaseDriver):
driver=self.__class__.__name__, driver=self.__class__.__name__,
reason=_('Unable to import pywsman library')) 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.power = power.DracPower()
self.boot = pxe.PXEBoot() self.boot = pxe.PXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy() self.deploy = iscsi_deploy.ISCSIDeploy()

View File

@ -182,6 +182,11 @@ class FakeDracDriver(base.BaseDriver):
driver=self.__class__.__name__, driver=self.__class__.__name__,
reason=_('Unable to import pywsman library')) 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.power = drac_power.DracPower()
self.deploy = fake.FakeDeploy() self.deploy = fake.FakeDeploy()
self.management = drac_mgmt.DracManagement() self.management = drac_mgmt.DracManagement()

View File

@ -22,6 +22,9 @@ from ironic.common.i18n import _
from ironic.common import utils from ironic.common import utils
pywsman = importutils.try_import('pywsman') pywsman = importutils.try_import('pywsman')
drac_client = importutils.try_import('dracclient.client')
drac_constants = importutils.try_import('dracclient.constants')
REQUIRED_PROPERTIES = { REQUIRED_PROPERTIES = {
'drac_host': _('IP address or hostname of the DRAC card. Required.'), 'drac_host': _('IP address or hostname of the DRAC card. Required.'),
@ -74,6 +77,10 @@ def parse_driver_info(node):
try: try:
parsed_driver_info['drac_protocol'] = str( parsed_driver_info['drac_protocol'] = str(
driver_info.get('drac_protocol', 'https')) 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: except UnicodeEncodeError:
error_msgs.append(_("'drac_protocol' contains non-ASCII symbol.")) error_msgs.append(_("'drac_protocol' contains non-ASCII symbol."))
@ -89,6 +96,25 @@ def parse_driver_info(node):
return parsed_driver_info 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): def find_xml(doc, item, namespace, find_all=False):
"""Find the first or all elements in an ElementTree object. """Find the first or all elements in an ElementTree object.

View File

@ -12,11 +12,10 @@
# under the License. # under the License.
""" """
DRAC Power Driver using the Base Server Profile DRAC power interface
""" """
from oslo_log import log as logging from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils from oslo_utils import importutils
from ironic.common import exception from ironic.common import exception
@ -24,80 +23,74 @@ from ironic.common.i18n import _LE
from ironic.common import states from ironic.common import states
from ironic.conductor import task_manager from ironic.conductor import task_manager
from ironic.drivers import base 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 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__) LOG = logging.getLogger(__name__)
POWER_STATES = { if drac_constants:
'2': states.POWER_ON, POWER_STATES = {
'3': states.POWER_OFF, drac_constants.POWER_ON: states.POWER_ON,
'11': states.REBOOT, 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): 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. :param node: an ironic node object.
:returns: power state, one of :mod: `ironic.common.states`. :returns: the power state, one of :mod:`ironic.common.states`.
:raises: DracClientError if the client received unexpected response.
:raises: InvalidParameterValue if required DRAC credentials are missing. :raises: InvalidParameterValue if required DRAC credentials are missing.
:raises: DracOperationError on an error from python-dracclient
""" """
client = drac_client.get_wsman_client(node) client = drac_common.get_drac_client(node)
filter_query = ('select EnabledState,ElementName from DCIM_ComputerSystem '
'where Name="srv:system"')
try: try:
doc = client.wsman_enumerate(resource_uris.DCIM_ComputerSystem, drac_power_state = client.get_power_state()
filter_query=filter_query) except drac_exceptions.BaseClientException as exc:
except exception.DracClientError as exc: LOG.error(_LE('DRAC driver failed to get power state for node '
with excutils.save_and_reraise_exception(): '%(node_uuid)s. Reason: %(error)s.'),
LOG.error(_LE('DRAC driver failed to get power state for node ' {'node_uuid': node.uuid, 'error': exc})
'%(node_uuid)s. Reason: %(error)s.'), raise exception.DracOperationError(error=exc)
{'node_uuid': node.uuid, 'error': exc})
enabled_state = drac_common.find_xml(doc, 'EnabledState', return POWER_STATES[drac_power_state]
resource_uris.DCIM_ComputerSystem)
return POWER_STATES[enabled_state.text]
def _set_power_state(node, target_state): def _set_power_state(node, power_state):
"""Turns the server power on/off or do a reboot. """Turns the server power on/off or do a reboot.
:param node: an ironic node object. :param node: an ironic node object.
:param target_state: target state of the node. :param power_state: a power state from :mod:`ironic.common.states`.
:raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are missing.
:raises: InvalidParameterValue if an invalid power state was specified. :raises: DracOperationError on an error from python-dracclient
""" """
client = drac_client.get_wsman_client(node) client = drac_common.get_drac_client(node)
selectors = {'CreationClassName': 'DCIM_ComputerSystem', target_power_state = REVERSE_POWER_STATES[power_state]
'Name': 'srv:system'}
properties = {'RequestedState': REVERSE_POWER_STATES[target_state]}
try: try:
client.wsman_invoke(resource_uris.DCIM_ComputerSystem, client.set_power_state(target_power_state)
'RequestStateChange', selectors, properties) except drac_exceptions.BaseClientException as exc:
except exception.DracRequestFailed as exc: LOG.error(_LE('DRAC driver failed to set power state for node '
with excutils.save_and_reraise_exception(): '%(node_uuid)s to %(power_state)s. '
LOG.error(_LE('DRAC driver failed to set power state for node ' 'Reason: %(error)s.'),
'%(node_uuid)s to %(target_power_state)s. ' {'node_uuid': node.uuid,
'Reason: %(error)s.'), 'power_state': power_state,
{'node_uuid': node.uuid, 'error': exc})
'target_power_state': target_state, raise exception.DracOperationError(error=exc)
'error': exc})
class DracPower(base.PowerInterface): class DracPower(base.PowerInterface):
"""Interface for power-related actions.""" """Interface for power-related actions."""
def get_properties(self): def get_properties(self):
"""Return the properties of the interface."""
return drac_common.COMMON_PROPERTIES return drac_common.COMMON_PROPERTIES
def validate(self, task): def validate(self, task):
@ -114,39 +107,36 @@ class DracPower(base.PowerInterface):
return drac_common.parse_driver_info(task.node) return drac_common.parse_driver_info(task.node)
def get_power_state(self, task): 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. :param task: a TaskManager instance containing the node to act on.
:returns: a power state. One of :mod:`ironic.common.states`. :returns: the power state, one of :mod:`ironic.common.states`.
:raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are
missing.
:raises: DracOperationError on an error from python-dracclient.
""" """
return _get_power_state(task.node) return _get_power_state(task.node)
@task_manager.require_exclusive_lock @task_manager.require_exclusive_lock
def set_power_state(self, task, power_state): 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 task: a TaskManager instance containing the node to act on.
:param power_state: Any power state from :mod:`ironic.common.states`. :param power_state: a power state from :mod:`ironic.common.states`.
:raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are
:raises: DracOperationFailed if the client received response with an missing.
error message. :raises: DracOperationError on an error from python-dracclient.
:raises: DracUnexpectedReturnValue if the client received a response
with unexpected return value.
""" """
_set_power_state(task.node, power_state) _set_power_state(task.node, power_state)
@task_manager.require_exclusive_lock @task_manager.require_exclusive_lock
def reboot(self, task): 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. :param task: a TaskManager instance containing the node to act on.
:raises: DracClientError if the client received unexpected response. :raises: InvalidParameterValue if required DRAC credentials are
:raises: DracOperationFailed if the client received response with an missing.
error message. :raises: DracOperationError on an error from python-dracclient.
:raises: DracUnexpectedReturnValue if the client received a response
with unexpected return value.
""" """
current_power_state = _get_power_state(task.node) current_power_state = _get_power_state(task.node)

View File

@ -17,6 +17,8 @@ Test class for common methods used by DRAC modules.
from xml.etree import ElementTree from xml.etree import ElementTree
import dracclient.client
import mock
from testtools.matchers import HasLength from testtools.matchers import HasLength
from ironic.common import exception from ironic.common import exception
@ -86,6 +88,15 @@ class DracCommonMethodsTestCase(db_base.DbTestCase):
info = drac_common.parse_driver_info(node) info = drac_common.parse_driver_info(node)
self.assertEqual('https', info.get('drac_protocol')) 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): def test_parse_driver_info_missing_username(self):
node = obj_utils.create_test_node(self.context, node = obj_utils.create_test_node(self.context,
driver='fake_drac', driver='fake_drac',
@ -102,6 +113,18 @@ class DracCommonMethodsTestCase(db_base.DbTestCase):
self.assertRaises(exception.InvalidParameterValue, self.assertRaises(exception.InvalidParameterValue,
drac_common.parse_driver_info, node) 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): def test_find_xml(self):
namespace = 'http://fake' namespace = 'http://fake'
value = 'fake_value' value = 'fake_value'

View File

@ -12,164 +12,107 @@
# under the License. # 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 import mock
from ironic.common import exception from ironic.common import exception
from ironic.common import states from ironic.common import states
from ironic.conductor import task_manager 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 common as drac_common
from ironic.drivers.modules.drac import power as drac_power 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.conductor import mgr_utils
from ironic.tests.unit.db import base from ironic.tests.unit.db import base
from ironic.tests.unit.db import utils as db_utils 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.objects import utils as obj_utils
from ironic.tests.unit.drivers import third_party_driver_mock_specs \
as mock_specs
INFO_DICT = db_utils.get_test_drac_info() INFO_DICT = db_utils.get_test_drac_info()
@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC) @mock.patch.object(drac_common, 'get_drac_client', spec_set=True,
@mock.patch.object(drac_power, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC) autospec=True)
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)
class DracPowerTestCase(base.DbTestCase): class DracPowerTestCase(base.DbTestCase):
def setUp(self): def setUp(self):
super(DracPowerTestCase, self).setUp() super(DracPowerTestCase, self).setUp()
driver_info = INFO_DICT mgr_utils.mock_the_extension_manager(driver='fake_drac')
mgr_utils.mock_the_extension_manager(driver="fake_drac") self.node = obj_utils.create_test_node(self.context,
self.node = db_utils.create_test_node( driver='fake_drac',
driver='fake_drac', driver_info=INFO_DICT)
driver_info=driver_info,
instance_uuid='instance_uuid_123')
def test_get_properties(self): def test_get_properties(self, mock_get_drac_client):
expected = drac_common.COMMON_PROPERTIES expected = drac_common.COMMON_PROPERTIES
driver = drac_power.DracPower() driver = drac_power.DracPower()
self.assertEqual(expected, driver.get_properties()) self.assertEqual(expected, driver.get_properties())
@mock.patch.object(drac_power, '_get_power_state', spec_set=True, def test_get_power_state(self, mock_get_drac_client):
autospec=True) mock_client = mock_get_drac_client.return_value
def test_get_power_state(self, mock_get_power_state): mock_client.get_power_state.return_value = drac_constants.POWER_ON
mock_get_power_state.return_value = states.POWER_ON
driver = drac_power.DracPower()
task = mock.Mock()
task.node.return_value = self.node
self.assertEqual(states.POWER_ON, driver.get_power_state(task)) with task_manager.acquire(self.context, self.node.uuid,
mock_get_power_state.assert_called_once_with(task.node) 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, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.power.set_power_state(task, states.POWER_ON) task.driver.power.set_power_state(task, states.POWER_OFF)
mock_set_power_state.assert_called_once_with(task.node,
states.POWER_ON) 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, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.power.reboot(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, drac_power_state = drac_power.REVERSE_POWER_STATES[states.REBOOT]
autospec=True) mock_client.set_power_state.assert_called_once_with(drac_power_state)
@mock.patch.object(drac_power, '_get_power_state', spec_set=True,
autospec=True) def test_reboot_while_powered_off(self, mock_get_drac_client):
def test_reboot_in_power_off(self, mock_get_power_state, mock_client = mock_get_drac_client.return_value
mock_set_power_state): mock_client.get_power_state.return_value = drac_constants.POWER_OFF
mock_get_power_state.return_value = states.POWER_OFF
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task: shared=False) as task:
task.driver.power.reboot(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)

View File

@ -16,6 +16,23 @@
"""This module provides mock 'specs' for third party modules that can be used """This module provides mock 'specs' for third party modules that can be used
when needing to mock those third party modules""" 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
IBOOT_SPEC = ( IBOOT_SPEC = (
'iBootInterface', 'iBootInterface',

View File

@ -28,6 +28,8 @@ Current list of mocked libraries:
- pysnmp - pysnmp
- scciclient - scciclient
- oneview_client - oneview_client
- pywsman
- python-dracclient
""" """
import sys import sys
@ -137,6 +139,29 @@ if not pywsman:
if 'ironic.drivers.modules.amt' in sys.modules: if 'ironic.drivers.modules.amt' in sys.modules:
six.moves.reload_module(sys.modules['ironic.drivers.modules.amt']) 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 # attempt to load the external 'iboot' library, which is required by
# the optional drivers.modules.iboot module # the optional drivers.modules.iboot module