diff --git a/doc/source/deploy/drivers.rst b/doc/source/deploy/drivers.rst
index 370dbbcd0c..db8654699b 100644
--- a/doc/source/deploy/drivers.rst
+++ b/doc/source/deploy/drivers.rst
@@ -21,7 +21,7 @@ DRAC with PXE deploy
- Add ``pxe_drac`` to the list of ``enabled_drivers`` in
``/etc/ironic/ironic.conf``
-- Install openwsman-python package
+- Install python-dracclient package
AMT
----
diff --git a/driver-requirements.txt b/driver-requirements.txt
index 686414e40b..827b8997e6 100644
--- a/driver-requirements.txt
+++ b/driver-requirements.txt
@@ -14,8 +14,8 @@ 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.
+# The amt driver import a python module called "pywsman", however, this does
+# not exist on pypi.
# It is installed by the openwsman-python (on RH) or python-openwsman (on deb)
# package, from https://github.com/Openwsman/openwsman/blob/master/bindings/python/Makefile.am#L29
# There is *also* a "wsman" module on pypi ... but I think that's the wrong one.
diff --git a/ironic/common/exception.py b/ironic/common/exception.py
index 00ce23a573..02cb97a1bd 100644
--- a/ironic/common/exception.py
+++ b/ironic/common/exception.py
@@ -488,38 +488,6 @@ class DracOperationError(IronicException):
_msg_fmt = _('DRAC operation failed. Reason: %(error)s')
-class DracRequestFailed(IronicException):
- pass
-
-
-class DracClientError(DracRequestFailed):
- _msg_fmt = _('DRAC client failed. '
- 'Last error (cURL error code): %(last_error)s, '
- 'fault string: "%(fault_string)s" '
- 'response_code: %(response_code)s')
-
-
-class DracOperationFailed(DracRequestFailed):
- _msg_fmt = _('DRAC operation failed. _msg_fmt: %(_msg_fmt)s')
-
-
-class DracUnexpectedReturnValue(DracRequestFailed):
- _msg_fmt = _('DRAC operation yielded return value %(actual_return_value)s '
- 'that is neither error nor expected '
- '%(expected_return_value)s')
-
-
-class DracPendingConfigJobExists(IronicException):
- _msg_fmt = _('Another job with ID %(job_id)s is already created '
- 'to configure %(target)s. Wait until existing job '
- 'is completed or is canceled')
-
-
-class DracInvalidFilterDialect(IronicException):
- _msg_fmt = _('Invalid filter dialect \'%(invalid_filter)s\'. '
- 'Supported options are %(supported)s')
-
-
class FailedToGetSensorData(IronicException):
_msg_fmt = _("Failed to get sensor data for node %(node)s. "
"Error: %(error)s")
diff --git a/ironic/drivers/drac.py b/ironic/drivers/drac.py
index 3447790a88..a9ff7095e9 100644
--- a/ironic/drivers/drac.py
+++ b/ironic/drivers/drac.py
@@ -33,11 +33,6 @@ class PXEDracDriver(base.BaseDriver):
"""Drac driver using PXE for deploy."""
def __init__(self):
- if not importutils.try_import('pywsman'):
- raise exception.DriverLoadError(
- driver=self.__class__.__name__,
- reason=_('Unable to import pywsman library'))
-
if not importutils.try_import('dracclient'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py
index 98178947fe..877674019b 100644
--- a/ironic/drivers/fake.py
+++ b/ironic/drivers/fake.py
@@ -178,11 +178,6 @@ class FakeDracDriver(base.BaseDriver):
"""Fake Drac driver."""
def __init__(self):
- if not importutils.try_import('pywsman'):
- raise exception.DriverLoadError(
- driver=self.__class__.__name__,
- reason=_('Unable to import pywsman library'))
-
if not importutils.try_import('dracclient'):
raise exception.DriverLoadError(
driver=self.__class__.__name__,
diff --git a/ironic/drivers/modules/drac/client.py b/ironic/drivers/modules/drac/client.py
deleted file mode 100644
index f868eff41d..0000000000
--- a/ironic/drivers/modules/drac/client.py
+++ /dev/null
@@ -1,268 +0,0 @@
-#
-# 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.
-
-"""
-Wrapper for pywsman.Client
-"""
-
-import time
-from xml.etree import ElementTree
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import importutils
-
-from ironic.common import exception
-from ironic.common.i18n import _
-from ironic.common.i18n import _LW
-from ironic.drivers.modules.drac import common as drac_common
-
-pywsman = importutils.try_import('pywsman')
-
-opts = [
- cfg.IntOpt('client_retry_count',
- default=5,
- help=_('In case there is a communication failure, the DRAC '
- 'client resends the request as many times as '
- 'defined in this setting.')),
- cfg.IntOpt('client_retry_delay',
- default=5,
- help=_('In case there is a communication failure, the DRAC '
- 'client waits for as many seconds as defined '
- 'in this setting before resending the request.'))
-]
-
-CONF = cfg.CONF
-opt_group = cfg.OptGroup(name='drac',
- title='Options for the DRAC driver')
-CONF.register_group(opt_group)
-CONF.register_opts(opts, opt_group)
-
-LOG = logging.getLogger(__name__)
-
-_SOAP_ENVELOPE_URI = 'http://www.w3.org/2003/05/soap-envelope'
-
-# Filter Dialects, see (Section 2.3.1):
-# http://en.community.dell.com/techcenter/extras/m/white_papers/20439105.aspx
-_FILTER_DIALECT_MAP = {'cql': 'http://schemas.dmtf.org/wbem/cql/1/dsp0202.pdf',
- 'wql': 'http://schemas.microsoft.com/wbem/wsman/1/WQL'}
-
-# ReturnValue constants
-RET_SUCCESS = '0'
-RET_ERROR = '2'
-RET_CREATED = '4096'
-
-
-def get_wsman_client(node):
- """Return a DRAC client object.
-
- Given an ironic node object, this method gives back a
- Client object which is a wrapper for pywsman.Client.
-
- :param node: an ironic node object.
- :returns: a Client object.
- :raises: InvalidParameterValue if some mandatory information
- is missing on the node or on invalid inputs.
- """
- driver_info = drac_common.parse_driver_info(node)
- client = Client(**driver_info)
- return client
-
-
-def retry_on_empty_response(client, action, *args, **kwargs):
- """Wrapper to retry an action on failure."""
-
- func = getattr(client, action)
- for i in range(CONF.drac.client_retry_count):
- response = func(*args, **kwargs)
- if response:
- return response
- else:
- LOG.warning(_LW('Empty response on calling %(action)s on client. '
- 'Last error (cURL error code): %(last_error)s, '
- 'fault string: "%(fault_string)s" '
- 'response_code: %(response_code)s. '
- 'Retry attempt %(count)d') %
- {'action': action,
- 'last_error': client.last_error(),
- 'fault_string': client.fault_string(),
- 'response_code': client.response_code(),
- 'count': i + 1})
-
- time.sleep(CONF.drac.client_retry_delay)
-
-
-class Client(object):
-
- def __init__(self, drac_host, drac_port, drac_path, drac_protocol,
- drac_username, drac_password):
- pywsman_client = pywsman.Client(drac_host, drac_port, drac_path,
- drac_protocol, drac_username,
- drac_password)
- # TODO(ifarkas): Add support for CACerts
- pywsman.wsman_transport_set_verify_peer(pywsman_client, False)
- pywsman.wsman_transport_set_verify_host(pywsman_client, False)
-
- self.client = pywsman_client
-
- def wsman_enumerate(self, resource_uri, filter_query=None,
- filter_dialect='cql'):
- """Enumerates a remote WS-Man class.
-
- :param resource_uri: URI of the resource.
- :param filter_query: the query string.
- :param filter_dialect: the filter dialect. Valid options are:
- 'cql' and 'wql'. Defaults to 'cql'.
- :raises: DracClientError on an error from pywsman library.
- :raises: DracInvalidFilterDialect if an invalid filter dialect
- was specified.
- :returns: an ElementTree object of the response received.
- """
- options = pywsman.ClientOptions()
-
- filter_ = None
- if filter_query is not None:
- try:
- filter_dialect = _FILTER_DIALECT_MAP[filter_dialect]
- except KeyError:
- valid_opts = ', '.join(_FILTER_DIALECT_MAP)
- raise exception.DracInvalidFilterDialect(
- invalid_filter=filter_dialect, supported=valid_opts)
-
- filter_ = pywsman.Filter()
- filter_.simple(filter_dialect, filter_query)
-
- options.set_flags(pywsman.FLAG_ENUMERATION_OPTIMIZATION)
- options.set_max_elements(100)
-
- doc = retry_on_empty_response(self.client, 'enumerate',
- options, filter_, resource_uri)
- root = self._get_root(doc)
- LOG.debug("WSMAN enumerate returned raw XML: %s",
- ElementTree.tostring(root))
-
- final_xml = root
- find_query = './/{%s}Body' % _SOAP_ENVELOPE_URI
- insertion_point = final_xml.find(find_query)
- while doc.context() is not None:
- doc = retry_on_empty_response(self.client, 'pull', options, None,
- resource_uri, str(doc.context()))
- root = self._get_root(doc)
- LOG.debug("WSMAN pull returned raw XML: %s",
- ElementTree.tostring(root))
-
- for result in root.findall(find_query):
- for child in list(result):
- insertion_point.append(child)
-
- return final_xml
-
- def wsman_invoke(self, resource_uri, method, selectors=None,
- properties=None, expected_return=None):
- """Invokes a remote WS-Man method.
-
- :param resource_uri: URI of the resource.
- :param method: name of the method to invoke.
- :param selectors: dictionary of selectors.
- :param properties: dictionary of properties.
- :param expected_return: expected return value.
- :raises: DracClientError on an error from pywsman library.
- :raises: DracOperationFailed on error reported back by DRAC.
- :raises: DracUnexpectedReturnValue on return value mismatch.
- :returns: an ElementTree object of the response received.
- """
- if selectors is None:
- selectors = {}
-
- if properties is None:
- properties = {}
-
- options = pywsman.ClientOptions()
-
- for name, value in selectors.items():
- options.add_selector(name, value)
-
- # NOTE(ifarkas): manually constructing the XML doc should be deleted
- # once pywsman supports passing a list as a property.
- # For now this is only a fallback method: in case no
- # list provided, the supported pywsman API will be used.
- list_included = any([isinstance(prop_item, list) for prop_item
- in properties.values()])
- if list_included:
- xml_doc = pywsman.XmlDoc('%s_INPUT' % method, resource_uri)
- xml_root = xml_doc.root()
-
- for name, value in properties.items():
- if isinstance(value, list):
- for item in value:
- xml_root.add(resource_uri, str(name), str(item))
- else:
- xml_root.add(resource_uri, name, value)
- LOG.debug(('WSMAN invoking: %(resource_uri)s:%(method)s'
- '\nselectors: %(selectors)r\nxml: %(xml)s'),
- {
- 'resource_uri': resource_uri,
- 'method': method,
- 'selectors': selectors,
- 'xml': xml_root.string()})
-
- else:
- xml_doc = None
-
- for name, value in properties.items():
- options.add_property(name, value)
-
- LOG.debug(('WSMAN invoking: %(resource_uri)s:%(method)s'
- '\nselectors: %(selectors)r\properties: %(props)r') % {
- 'resource_uri': resource_uri,
- 'method': method,
- 'selectors': selectors,
- 'props': properties})
-
- doc = retry_on_empty_response(self.client, 'invoke', options,
- resource_uri, method, xml_doc)
- root = self._get_root(doc)
- LOG.debug("WSMAN invoke returned raw XML: %s",
- ElementTree.tostring(root))
-
- return_value = drac_common.find_xml(root, 'ReturnValue',
- resource_uri).text
- if return_value == RET_ERROR:
- messages = drac_common.find_xml(root, 'Message',
- resource_uri, True)
- message_args = drac_common.find_xml(root, 'MessageArguments',
- resource_uri, True)
-
- if message_args:
- messages = [m.text % p.text for (m, p) in
- zip(messages, message_args)]
- else:
- messages = [m.text for m in messages]
-
- raise exception.DracOperationFailed(message='%r' % messages)
-
- if expected_return and return_value != expected_return:
- raise exception.DracUnexpectedReturnValue(
- expected_return_value=expected_return,
- actual_return_value=return_value)
-
- return root
-
- def _get_root(self, doc):
- if doc is None or doc.root() is None:
- raise exception.DracClientError(
- last_error=self.client.last_error(),
- fault_string=self.client.fault_string(),
- response_code=self.client.response_code())
- root = doc.root()
- return ElementTree.fromstring(root.string())
diff --git a/ironic/drivers/modules/drac/common.py b/ironic/drivers/modules/drac/common.py
index 9a0a0f071d..ead3a88724 100644
--- a/ironic/drivers/modules/drac/common.py
+++ b/ironic/drivers/modules/drac/common.py
@@ -21,7 +21,6 @@ from ironic.common import exception
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')
@@ -113,24 +112,3 @@ def get_drac_client(node):
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.
-
- :param doc: the element tree object.
- :param item: the element name.
- :param namespace: the namespace of the element.
- :param find_all: Boolean value, if True find all elements, if False
- find only the first one. Defaults to False.
- :returns: if find_all is False the element object will be returned
- if found, None if not found. If find_all is True a list of
- element objects will be returned or an empty list if no
- elements were found.
-
- """
- query = ('.//{%(namespace)s}%(item)s' % {'namespace': namespace,
- 'item': item})
- if find_all:
- return doc.findall(query)
- return doc.find(query)
diff --git a/ironic/drivers/modules/drac/resource_uris.py b/ironic/drivers/modules/drac/resource_uris.py
deleted file mode 100644
index d8e2369c09..0000000000
--- a/ironic/drivers/modules/drac/resource_uris.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# 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.
-
-"""
-Resource URIs and helper functions for the classes implemented by the DRAC
-WS-Man API.
-"""
-
-DCIM_ComputerSystem = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2'
- '/DCIM_ComputerSystem')
-
-DCIM_BootSourceSetting = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BootSourceSetting')
-
-DCIM_BootConfigSetting = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BootConfigSetting')
-
-DCIM_BIOSService = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BIOSService')
-
-DCIM_BIOSEnumeration = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BIOSEnumeration')
-DCIM_BIOSString = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BIOSString')
-DCIM_BIOSInteger = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_BIOSInteger')
-
-DCIM_LifecycleJob = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_LifecycleJob')
-
-DCIM_SystemView = ('http://schemas.dell.com/wbem/wscim/1/cim-schema/2/'
- 'DCIM_SystemView')
-
-CIM_XmlSchema = 'http://www.w3.org/2001/XMLSchema-instance'
-
-CIM_WSMAN = 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd'
diff --git a/ironic/tests/unit/drivers/modules/amt/test_common.py b/ironic/tests/unit/drivers/modules/amt/test_common.py
index f83ce9c31a..ad70873b07 100644
--- a/ironic/tests/unit/drivers/modules/amt/test_common.py
+++ b/ironic/tests/unit/drivers/modules/amt/test_common.py
@@ -27,7 +27,7 @@ from ironic.drivers.modules.amt import resource_uris
from ironic.tests import base
from ironic.tests.unit.db import base as db_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.modules.amt 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
diff --git a/ironic/tests/unit/drivers/modules/amt/test_management.py b/ironic/tests/unit/drivers/modules/amt/test_management.py
index f79e26bec5..7b0cfb6a21 100644
--- a/ironic/tests/unit/drivers/modules/amt/test_management.py
+++ b/ironic/tests/unit/drivers/modules/amt/test_management.py
@@ -27,7 +27,7 @@ from ironic.drivers.modules.amt import resource_uris
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_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.modules.amt 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
diff --git a/ironic/tests/unit/drivers/modules/amt/test_power.py b/ironic/tests/unit/drivers/modules/amt/test_power.py
index f509bfd442..041ad28879 100644
--- a/ironic/tests/unit/drivers/modules/amt/test_power.py
+++ b/ironic/tests/unit/drivers/modules/amt/test_power.py
@@ -29,7 +29,7 @@ from ironic.drivers.modules.amt import resource_uris
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_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.modules.amt import utils as test_utils
from ironic.tests.unit.objects import utils as obj_utils
INFO_DICT = db_utils.get_test_amt_info()
diff --git a/ironic/tests/unit/drivers/modules/drac/utils.py b/ironic/tests/unit/drivers/modules/amt/utils.py
similarity index 100%
rename from ironic/tests/unit/drivers/modules/drac/utils.py
rename to ironic/tests/unit/drivers/modules/amt/utils.py
diff --git a/ironic/tests/unit/drivers/modules/drac/test_client.py b/ironic/tests/unit/drivers/modules/drac/test_client.py
deleted file mode 100644
index e3755c7270..0000000000
--- a/ironic/tests/unit/drivers/modules/drac/test_client.py
+++ /dev/null
@@ -1,256 +0,0 @@
-#
-# 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.
-
-"""
-Test class for DRAC client wrapper.
-"""
-
-import time
-from xml.etree import ElementTree
-
-import mock
-
-from ironic.common import exception
-from ironic.drivers.modules.drac import client as drac_client
-from ironic.tests 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
-
-INFO_DICT = db_utils.get_test_drac_info()
-
-
-@mock.patch.object(drac_client, 'pywsman', spec_set=mock_specs.PYWSMAN_SPEC)
-class DracClientTestCase(base.TestCase):
-
- def setUp(self):
- super(DracClientTestCase, self).setUp()
- self.resource_uri = 'http://foo/wsman'
-
- def test_wsman_enumerate(self, mock_client_pywsman):
- mock_xml = test_utils.mock_wsman_root('')
- mock_pywsman_client = mock_client_pywsman.Client.return_value
- mock_pywsman_client.enumerate.return_value = mock_xml
-
- client = drac_client.Client(**INFO_DICT)
- client.wsman_enumerate(self.resource_uri)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_options.set_flags.assert_called_once_with(
- mock_client_pywsman.FLAG_ENUMERATION_OPTIMIZATION)
- mock_options.set_max_elements.assert_called_once_with(100)
- mock_pywsman_client.enumerate.assert_called_once_with(
- mock_options, None, self.resource_uri)
- mock_xml.context.assert_called_once_with()
-
- @mock.patch.object(time, 'sleep', lambda seconds: None)
- def test_wsman_enumerate_retry(self, mock_client_pywsman):
- mock_xml = test_utils.mock_wsman_root('')
- mock_pywsman_client = mock_client_pywsman.Client.return_value
- mock_pywsman_client.enumerate.side_effect = [None, mock_xml]
-
- client = drac_client.Client(**INFO_DICT)
- client.wsman_enumerate(self.resource_uri)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_options.set_flags.assert_called_once_with(
- mock_client_pywsman.FLAG_ENUMERATION_OPTIMIZATION)
- mock_options.set_max_elements.assert_called_once_with(100)
- mock_pywsman_client.enumerate.assert_has_calls([
- mock.call(mock_options, None, self.resource_uri),
- mock.call(mock_options, None, self.resource_uri)
- ])
- mock_xml.context.assert_called_once_with()
-
- def test_wsman_enumerate_with_additional_pull(self, mock_client_pywsman):
- mock_root = mock.Mock(spec=['string'])
- mock_root.string.side_effect = [
- test_utils.build_soap_xml([{'item1': 'test1'}]),
- test_utils.build_soap_xml([{'item2': 'test2'}])
- ]
- mock_xml = mock.Mock(spec=['context', 'root'])
- mock_xml.root.return_value = mock_root
- mock_xml.context.side_effect = [42, 42, None]
-
- mock_pywsman_client = mock_client_pywsman.Client.return_value
- mock_pywsman_client.enumerate.return_value = mock_xml
- mock_pywsman_client.pull.return_value = mock_xml
-
- client = drac_client.Client(**INFO_DICT)
- result = client.wsman_enumerate(self.resource_uri)
-
- # assert the XML was merged
- result_string = ElementTree.tostring(result)
- self.assertIn(b'test1', result_string)
- self.assertIn(b'test2', result_string)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_options.set_flags.assert_called_once_with(
- mock_client_pywsman.FLAG_ENUMERATION_OPTIMIZATION)
- mock_options.set_max_elements.assert_called_once_with(100)
- mock_pywsman_client.enumerate.assert_called_once_with(
- mock_options, None, self.resource_uri)
-
- def test_wsman_enumerate_filter_query(self, mock_client_pywsman):
- mock_xml = test_utils.mock_wsman_root('')
- mock_pywsman_client = mock_client_pywsman.Client.return_value
- mock_pywsman_client.enumerate.return_value = mock_xml
-
- client = drac_client.Client(**INFO_DICT)
- filter_query = 'SELECT * FROM foo'
- client.wsman_enumerate(self.resource_uri, filter_query=filter_query)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_filter = mock_client_pywsman.Filter.return_value
- mock_filter.simple.assert_called_once_with(mock.ANY, filter_query)
- mock_pywsman_client.enumerate.assert_called_once_with(
- mock_options, mock_filter, self.resource_uri)
- mock_xml.context.assert_called_once_with()
-
- def test_wsman_enumerate_invalid_filter_dialect(self, mock_client_pywsman):
- client = drac_client.Client(**INFO_DICT)
- self.assertRaises(exception.DracInvalidFilterDialect,
- client.wsman_enumerate, self.resource_uri,
- filter_query='foo',
- filter_dialect='invalid')
-
- def test_wsman_invoke(self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_SUCCESS}], self.resource_uri)
- 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
-
- method_name = 'method'
- client = drac_client.Client(**INFO_DICT)
- client.wsman_invoke(self.resource_uri, method_name)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, None)
-
- @mock.patch.object(time, 'sleep', lambda seconds: None)
- def test_wsman_invoke_retry(self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_SUCCESS}], self.resource_uri)
- mock_xml = test_utils.mock_wsman_root(result_xml)
- mock_pywsman_client = mock_client_pywsman.Client.return_value
- mock_pywsman_client.invoke.side_effect = [None, mock_xml]
-
- method_name = 'method'
- client = drac_client.Client(**INFO_DICT)
- client.wsman_invoke(self.resource_uri, method_name)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_has_calls([
- mock.call(mock_options, self.resource_uri, method_name, None),
- mock.call(mock_options, self.resource_uri, method_name, None)
- ])
-
- def test_wsman_invoke_with_selectors(self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_SUCCESS}], self.resource_uri)
- 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
-
- method_name = 'method'
- selectors = {'foo': 'bar'}
- client = drac_client.Client(**INFO_DICT)
- client.wsman_invoke(self.resource_uri, method_name,
- selectors=selectors)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, None)
- mock_options.add_selector.assert_called_once_with('foo', 'bar')
-
- def test_wsman_invoke_with_properties(self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_SUCCESS}], self.resource_uri)
- 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
-
- method_name = 'method'
- properties = {'foo': 'bar'}
- client = drac_client.Client(**INFO_DICT)
- client.wsman_invoke(self.resource_uri, method_name,
- properties=properties)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, None)
- mock_options.add_property.assert_called_once_with('foo', 'bar')
-
- def test_wsman_invoke_with_properties_including_a_list(
- self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_SUCCESS}], self.resource_uri)
- 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_request_xml = mock_client_pywsman.XmlDoc.return_value
-
- method_name = 'method'
- properties = {'foo': ['bar', 'baz']}
- client = drac_client.Client(**INFO_DICT)
- client.wsman_invoke(self.resource_uri, method_name,
- properties=properties)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, mock_request_xml)
- mock_request_xml.root().add.assert_has_calls([
- mock.call(self.resource_uri, 'foo', 'bar'),
- mock.call(self.resource_uri, 'foo', 'baz')
- ])
- self.assertEqual(2, mock_request_xml.root().add.call_count)
-
- def test_wsman_invoke_receives_error_return_value(
- self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': drac_client.RET_ERROR,
- 'Message': 'error message'}],
- self.resource_uri)
- 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
-
- method_name = 'method'
- client = drac_client.Client(**INFO_DICT)
- self.assertRaises(exception.DracOperationFailed,
- client.wsman_invoke, self.resource_uri, method_name)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, None)
-
- def test_wsman_invoke_receives_unexpected_return_value(
- self, mock_client_pywsman):
- result_xml = test_utils.build_soap_xml(
- [{'ReturnValue': '42'}], self.resource_uri)
- 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
-
- method_name = 'method'
- client = drac_client.Client(**INFO_DICT)
- self.assertRaises(exception.DracUnexpectedReturnValue,
- client.wsman_invoke, self.resource_uri, method_name,
- {}, {}, drac_client.RET_SUCCESS)
-
- mock_options = mock_client_pywsman.ClientOptions.return_value
- mock_pywsman_client.invoke.assert_called_once_with(
- mock_options, self.resource_uri, method_name, None)
diff --git a/ironic/tests/unit/drivers/modules/drac/test_common.py b/ironic/tests/unit/drivers/modules/drac/test_common.py
index 0e18a51ae9..31edb0f2a7 100644
--- a/ironic/tests/unit/drivers/modules/drac/test_common.py
+++ b/ironic/tests/unit/drivers/modules/drac/test_common.py
@@ -15,11 +15,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
from ironic.drivers.modules.drac import common as drac_common
@@ -124,35 +121,3 @@ class DracCommonMethodsTestCase(db_base.DbTestCase):
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'
- test_doc = ElementTree.fromstring("""
-
- %(value)s
-
- """ % {'ns': namespace, 'value': value})
-
- result = drac_common.find_xml(test_doc, 'test_element', namespace)
- self.assertEqual(value, result.text)
-
- def test_find_xml_find_all(self):
- namespace = 'http://fake'
- value1 = 'fake_value1'
- value2 = 'fake_value2'
- test_doc = ElementTree.fromstring("""
-
- %(value1)s
- meow
- %(value2)s
- bark
-
- """ % {'ns': namespace, 'value1': value1,
- 'value2': value2})
-
- result = drac_common.find_xml(test_doc, 'test_element',
- namespace, find_all=True)
- self.assertThat(result, HasLength(2))
- result_text = [v.text for v in result]
- self.assertEqual(sorted([value1, value2]), sorted(result_text))
diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py
index 5a698bc241..40f20cbcd3 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mocks.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py
@@ -127,21 +127,18 @@ if not oneview_client:
# attempt to load the external 'pywsman' library, which is required by
-# the optional drivers.modules.drac and drivers.modules.amt module
+# the optional drivers.modules.amt module
pywsman = importutils.try_import('pywsman')
if not pywsman:
pywsman = mock.MagicMock(spec_set=mock_specs.PYWSMAN_SPEC)
sys.modules['pywsman'] = pywsman
# 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'])
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'
+# by the optional drivers.modules.drac module
dracclient = importutils.try_import('dracclient')
if not dracclient:
dracclient = mock.MagicMock(spec_set=mock_specs.DRACCLIENT_SPEC)
diff --git a/releasenotes/notes/drac-migrate-to-dracclient-2bd8a6d1dd3fdc69.yaml b/releasenotes/notes/drac-migrate-to-dracclient-2bd8a6d1dd3fdc69.yaml
new file mode 100644
index 0000000000..2f623600ae
--- /dev/null
+++ b/releasenotes/notes/drac-migrate-to-dracclient-2bd8a6d1dd3fdc69.yaml
@@ -0,0 +1,13 @@
+---
+fixes:
+ - DRAC driver migrated from ``pywsman`` to ``python-dracclient`` fixing
+ the driver lockup issue caused by the python interpreter not handling
+ signals when execution handed to the c library.
+ - Fixes an issue with setting the boot device multiple times without a reboot
+ in the DRAC driver by setting the boot device only before power management
+ operations.
+upgrade:
+ - Dependency for DRAC driver changed from ``pywsman`` to
+ ``python-dracclient``. Exceptions thrown by the driver and return values of
+ the ``set_bios_config``, ``commit_bios_config`` and ``abandon_bios_config``
+ methods changed on the vendor-passthru interface.