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-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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user