Merge "Fix immediate failure on SSL errors"
This commit is contained in:
commit
e7122263c4
|
@ -38,7 +38,7 @@ class DRACClient(object):
|
|||
BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1'
|
||||
|
||||
def __init__(self, host, username, password, port=443, path='/wsman',
|
||||
protocol='https'):
|
||||
protocol='https', retries=3, retry_delay=0):
|
||||
"""Creates client object
|
||||
|
||||
:param host: hostname or IP of the DRAC interface
|
||||
|
@ -47,9 +47,11 @@ class DRACClient(object):
|
|||
:param port: port for accessing the DRAC interface
|
||||
:param path: path for accessing the DRAC interface
|
||||
:param protocol: protocol for accessing the DRAC interface
|
||||
:param retries: number of resends to attempt on failure
|
||||
:param retry_delay: number of seconds to wait between retries
|
||||
"""
|
||||
self.client = WSManClient(host, username, password, port, path,
|
||||
protocol)
|
||||
protocol, retries, retry_delay)
|
||||
self._job_mgmt = job.JobManagement(self.client)
|
||||
self._power_mgmt = bios.PowerManagement(self.client)
|
||||
self._boot_mgmt = bios.BootManagement(self.client)
|
||||
|
|
|
@ -17,6 +17,7 @@ import uuid
|
|||
import lxml.etree
|
||||
import lxml.objectify
|
||||
import mock
|
||||
import requests.exceptions
|
||||
import requests_mock
|
||||
|
||||
from dracclient import exceptions
|
||||
|
@ -108,6 +109,74 @@ class ClientTestCase(base.BaseTest):
|
|||
|
||||
self.assertEqual('yay!', resp.text)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_invoke_with_ssl_errors(self, mock_requests):
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
exc=requests.exceptions.SSLError)
|
||||
|
||||
self.assertRaises(exceptions.WSManRequestFailure,
|
||||
self.client.invoke, 'http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_invoke_with_ssl_error_success(self, mock_requests):
|
||||
expected_resp = '<result>yay!</result>'
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
[{'exc': requests.exceptions.SSLError},
|
||||
{'text': expected_resp}])
|
||||
|
||||
resp = self.client.invoke('http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
self.assertEqual('yay!', resp.text)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_invoke_with_connection_errors(self, mock_requests):
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
exc=requests.exceptions.ConnectionError)
|
||||
|
||||
self.assertRaises(exceptions.WSManRequestFailure,
|
||||
self.client.invoke, 'http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_invoke_with_connection_error_success(self, mock_requests):
|
||||
expected_resp = '<result>yay!</result>'
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
[{'exc': requests.exceptions.ConnectionError},
|
||||
{'text': expected_resp}])
|
||||
|
||||
resp = self.client.invoke('http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
self.assertEqual('yay!', resp.text)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_invoke_with_unknown_error(self, mock_requests):
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
exc=requests.exceptions.HTTPError)
|
||||
self.assertRaises(exceptions.WSManRequestFailure,
|
||||
self.client.invoke, 'http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
@requests_mock.Mocker()
|
||||
@mock.patch('time.sleep', autospec=True)
|
||||
def test_client_retry_delay(self, mock_requests, mock_ts):
|
||||
retry_delay = 5
|
||||
fake_endpoint = test_utils.FAKE_ENDPOINT.copy()
|
||||
fake_endpoint['retry_delay'] = retry_delay
|
||||
client = dracclient.wsman.Client(**fake_endpoint)
|
||||
expected_resp = '<result>yay!</result>'
|
||||
mock_requests.post('https://1.2.3.4:443/wsman',
|
||||
[{'exc': requests.exceptions.SSLError},
|
||||
{'text': expected_resp}])
|
||||
|
||||
resp = client.invoke('http://resource', 'method',
|
||||
{'selector': 'foo'}, {'property': 'bar'})
|
||||
|
||||
self.assertEqual('yay!', resp.text)
|
||||
mock_ts.assert_called_once_with(retry_delay)
|
||||
|
||||
|
||||
class PayloadTestCase(base.BaseTest):
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
# under the License.
|
||||
|
||||
import logging
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from lxml import etree as ElementTree
|
||||
import requests
|
||||
import requests.exceptions
|
||||
|
||||
from dracclient import exceptions
|
||||
|
@ -41,13 +41,27 @@ class Client(object):
|
|||
"""Simple client for talking over WSMan protocol."""
|
||||
|
||||
def __init__(self, host, username, password, port=443, path='/wsman',
|
||||
protocol='https'):
|
||||
protocol='https', retries=3, retry_delay=0):
|
||||
"""Creates client object
|
||||
|
||||
:param host: hostname or IP of the DRAC interface
|
||||
:param username: username for accessing the DRAC interface
|
||||
:param password: password for accessing the DRAC interface
|
||||
:param port: port for accessing the DRAC interface
|
||||
:param path: path for accessing the DRAC interface
|
||||
:param protocol: protocol for accessing the DRAC interface
|
||||
:param retries: number of resends to attempt on failure
|
||||
:param retry_delay: number of seconds to wait between retries
|
||||
"""
|
||||
|
||||
self.host = host
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.port = port
|
||||
self.path = path
|
||||
self.protocol = protocol
|
||||
self.retries = retries
|
||||
self.retry_delay = retry_delay
|
||||
self.endpoint = ('%(protocol)s://%(host)s:%(port)s%(path)s' % {
|
||||
'protocol': self.protocol,
|
||||
'host': self.host,
|
||||
|
@ -58,16 +72,52 @@ class Client(object):
|
|||
payload = payload.build()
|
||||
LOG.debug('Sending request to %(endpoint)s: %(payload)s',
|
||||
{'endpoint': self.endpoint, 'payload': payload})
|
||||
try:
|
||||
resp = requests.post(
|
||||
self.endpoint,
|
||||
auth=requests.auth.HTTPBasicAuth(self.username, self.password),
|
||||
data=payload,
|
||||
# TODO(ifarkas): enable cert verification
|
||||
verify=False)
|
||||
except requests.exceptions.RequestException:
|
||||
LOG.exception('Request failed')
|
||||
raise exceptions.WSManRequestFailure()
|
||||
|
||||
num_tries = 1
|
||||
while num_tries <= self.retries:
|
||||
try:
|
||||
resp = requests.post(
|
||||
self.endpoint,
|
||||
auth=requests.auth.HTTPBasicAuth(self.username,
|
||||
self.password),
|
||||
data=payload,
|
||||
# TODO(ifarkas): enable cert verification
|
||||
verify=False)
|
||||
break
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.SSLError) as ex:
|
||||
|
||||
error_msg = "A {error_type} error occurred while " \
|
||||
" communicating with {host}, attempt {num_tries} of " \
|
||||
"{retries}".format(
|
||||
error_type=type(ex).__name__,
|
||||
host=self.host,
|
||||
num_tries=num_tries,
|
||||
retries=self.retries)
|
||||
|
||||
if num_tries == self.retries:
|
||||
LOG.error(error_msg)
|
||||
raise exceptions.WSManRequestFailure(
|
||||
"A {error_type} error occurred while communicating "
|
||||
"with {host}: {error}".format(
|
||||
error_type=type(ex).__name__,
|
||||
host=self.host,
|
||||
error=ex))
|
||||
else:
|
||||
LOG.warning(error_msg)
|
||||
|
||||
num_tries += 1
|
||||
if self.retry_delay > 0 and num_tries <= self.retries:
|
||||
time.sleep(self.retry_delay)
|
||||
|
||||
except requests.exceptions.RequestException as ex:
|
||||
error_msg = "A {error_type} error occurred while " \
|
||||
"communicating with {host}: {error}".format(
|
||||
error_type=type(ex).__name__,
|
||||
host=self.host,
|
||||
error=ex)
|
||||
LOG.error(error_msg)
|
||||
raise exceptions.WSManRequestFailure(error_msg)
|
||||
|
||||
LOG.debug('Received response from %(endpoint)s: %(payload)s',
|
||||
{'endpoint': self.endpoint, 'payload': resp.content})
|
||||
|
|
Loading…
Reference in New Issue