summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2017-06-16 21:48:48 +0000
committerGerrit Code Review <review@openstack.org>2017-06-16 21:48:48 +0000
commite7122263c4cbc9a3e2c37ce47481ff7ba7ca2a31 (patch)
treed5c3c659f726ac339e481a4368073f557bbc2c5d
parent39253bb272a7d4cfcc161c19708b8c6949a21240 (diff)
parentd6edaac2a1fc0fbb3cfc943a3a1845c74fc57c4f (diff)
Merge "Fix immediate failure on SSL errors"HEADmaster
-rw-r--r--dracclient/client.py6
-rw-r--r--dracclient/tests/test_wsman.py69
-rw-r--r--dracclient/wsman.py74
3 files changed, 135 insertions, 14 deletions
diff --git a/dracclient/client.py b/dracclient/client.py
index 7358c86..cc0968e 100644
--- a/dracclient/client.py
+++ b/dracclient/client.py
@@ -38,7 +38,7 @@ class DRACClient(object):
38 BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1' 38 BIOS_DEVICE_FQDD = 'BIOS.Setup.1-1'
39 39
40 def __init__(self, host, username, password, port=443, path='/wsman', 40 def __init__(self, host, username, password, port=443, path='/wsman',
41 protocol='https'): 41 protocol='https', retries=3, retry_delay=0):
42 """Creates client object 42 """Creates client object
43 43
44 :param host: hostname or IP of the DRAC interface 44 :param host: hostname or IP of the DRAC interface
@@ -47,9 +47,11 @@ class DRACClient(object):
47 :param port: port for accessing the DRAC interface 47 :param port: port for accessing the DRAC interface
48 :param path: path for accessing the DRAC interface 48 :param path: path for accessing the DRAC interface
49 :param protocol: protocol for accessing the DRAC interface 49 :param protocol: protocol for accessing the DRAC interface
50 :param retries: number of resends to attempt on failure
51 :param retry_delay: number of seconds to wait between retries
50 """ 52 """
51 self.client = WSManClient(host, username, password, port, path, 53 self.client = WSManClient(host, username, password, port, path,
52 protocol) 54 protocol, retries, retry_delay)
53 self._job_mgmt = job.JobManagement(self.client) 55 self._job_mgmt = job.JobManagement(self.client)
54 self._power_mgmt = bios.PowerManagement(self.client) 56 self._power_mgmt = bios.PowerManagement(self.client)
55 self._boot_mgmt = bios.BootManagement(self.client) 57 self._boot_mgmt = bios.BootManagement(self.client)
diff --git a/dracclient/tests/test_wsman.py b/dracclient/tests/test_wsman.py
index 5f008f9..a76b582 100644
--- a/dracclient/tests/test_wsman.py
+++ b/dracclient/tests/test_wsman.py
@@ -17,6 +17,7 @@ import uuid
17import lxml.etree 17import lxml.etree
18import lxml.objectify 18import lxml.objectify
19import mock 19import mock
20import requests.exceptions
20import requests_mock 21import requests_mock
21 22
22from dracclient import exceptions 23from dracclient import exceptions
@@ -108,6 +109,74 @@ class ClientTestCase(base.BaseTest):
108 109
109 self.assertEqual('yay!', resp.text) 110 self.assertEqual('yay!', resp.text)
110 111
112 @requests_mock.Mocker()
113 def test_invoke_with_ssl_errors(self, mock_requests):
114 mock_requests.post('https://1.2.3.4:443/wsman',
115 exc=requests.exceptions.SSLError)
116
117 self.assertRaises(exceptions.WSManRequestFailure,
118 self.client.invoke, 'http://resource', 'method',
119 {'selector': 'foo'}, {'property': 'bar'})
120
121 @requests_mock.Mocker()
122 def test_invoke_with_ssl_error_success(self, mock_requests):
123 expected_resp = '<result>yay!</result>'
124 mock_requests.post('https://1.2.3.4:443/wsman',
125 [{'exc': requests.exceptions.SSLError},
126 {'text': expected_resp}])
127
128 resp = self.client.invoke('http://resource', 'method',
129 {'selector': 'foo'}, {'property': 'bar'})
130
131 self.assertEqual('yay!', resp.text)
132
133 @requests_mock.Mocker()
134 def test_invoke_with_connection_errors(self, mock_requests):
135 mock_requests.post('https://1.2.3.4:443/wsman',
136 exc=requests.exceptions.ConnectionError)
137
138 self.assertRaises(exceptions.WSManRequestFailure,
139 self.client.invoke, 'http://resource', 'method',
140 {'selector': 'foo'}, {'property': 'bar'})
141
142 @requests_mock.Mocker()
143 def test_invoke_with_connection_error_success(self, mock_requests):
144 expected_resp = '<result>yay!</result>'
145 mock_requests.post('https://1.2.3.4:443/wsman',
146 [{'exc': requests.exceptions.ConnectionError},
147 {'text': expected_resp}])
148
149 resp = self.client.invoke('http://resource', 'method',
150 {'selector': 'foo'}, {'property': 'bar'})
151
152 self.assertEqual('yay!', resp.text)
153
154 @requests_mock.Mocker()
155 def test_invoke_with_unknown_error(self, mock_requests):
156 mock_requests.post('https://1.2.3.4:443/wsman',
157 exc=requests.exceptions.HTTPError)
158 self.assertRaises(exceptions.WSManRequestFailure,
159 self.client.invoke, 'http://resource', 'method',
160 {'selector': 'foo'}, {'property': 'bar'})
161
162 @requests_mock.Mocker()
163 @mock.patch('time.sleep', autospec=True)
164 def test_client_retry_delay(self, mock_requests, mock_ts):
165 retry_delay = 5
166 fake_endpoint = test_utils.FAKE_ENDPOINT.copy()
167 fake_endpoint['retry_delay'] = retry_delay
168 client = dracclient.wsman.Client(**fake_endpoint)
169 expected_resp = '<result>yay!</result>'
170 mock_requests.post('https://1.2.3.4:443/wsman',
171 [{'exc': requests.exceptions.SSLError},
172 {'text': expected_resp}])
173
174 resp = client.invoke('http://resource', 'method',
175 {'selector': 'foo'}, {'property': 'bar'})
176
177 self.assertEqual('yay!', resp.text)
178 mock_ts.assert_called_once_with(retry_delay)
179
111 180
112class PayloadTestCase(base.BaseTest): 181class PayloadTestCase(base.BaseTest):
113 182
diff --git a/dracclient/wsman.py b/dracclient/wsman.py
index cd94f02..0799a8d 100644
--- a/dracclient/wsman.py
+++ b/dracclient/wsman.py
@@ -12,10 +12,10 @@
12# under the License. 12# under the License.
13 13
14import logging 14import logging
15import time
15import uuid 16import uuid
16 17
17from lxml import etree as ElementTree 18from lxml import etree as ElementTree
18import requests
19import requests.exceptions 19import requests.exceptions
20 20
21from dracclient import exceptions 21from dracclient import exceptions
@@ -41,13 +41,27 @@ class Client(object):
41 """Simple client for talking over WSMan protocol.""" 41 """Simple client for talking over WSMan protocol."""
42 42
43 def __init__(self, host, username, password, port=443, path='/wsman', 43 def __init__(self, host, username, password, port=443, path='/wsman',
44 protocol='https'): 44 protocol='https', retries=3, retry_delay=0):
45 """Creates client object
46
47 :param host: hostname or IP of the DRAC interface
48 :param username: username for accessing the DRAC interface
49 :param password: password for accessing the DRAC interface
50 :param port: port for accessing the DRAC interface
51 :param path: path for accessing the DRAC interface
52 :param protocol: protocol for accessing the DRAC interface
53 :param retries: number of resends to attempt on failure
54 :param retry_delay: number of seconds to wait between retries
55 """
56
45 self.host = host 57 self.host = host
46 self.username = username 58 self.username = username
47 self.password = password 59 self.password = password
48 self.port = port 60 self.port = port
49 self.path = path 61 self.path = path
50 self.protocol = protocol 62 self.protocol = protocol
63 self.retries = retries
64 self.retry_delay = retry_delay
51 self.endpoint = ('%(protocol)s://%(host)s:%(port)s%(path)s' % { 65 self.endpoint = ('%(protocol)s://%(host)s:%(port)s%(path)s' % {
52 'protocol': self.protocol, 66 'protocol': self.protocol,
53 'host': self.host, 67 'host': self.host,
@@ -58,16 +72,52 @@ class Client(object):
58 payload = payload.build() 72 payload = payload.build()
59 LOG.debug('Sending request to %(endpoint)s: %(payload)s', 73 LOG.debug('Sending request to %(endpoint)s: %(payload)s',
60 {'endpoint': self.endpoint, 'payload': payload}) 74 {'endpoint': self.endpoint, 'payload': payload})
61 try: 75
62 resp = requests.post( 76 num_tries = 1
63 self.endpoint, 77 while num_tries <= self.retries:
64 auth=requests.auth.HTTPBasicAuth(self.username, self.password), 78 try:
65 data=payload, 79 resp = requests.post(
66 # TODO(ifarkas): enable cert verification 80 self.endpoint,
67 verify=False) 81 auth=requests.auth.HTTPBasicAuth(self.username,
68 except requests.exceptions.RequestException: 82 self.password),
69 LOG.exception('Request failed') 83 data=payload,
70 raise exceptions.WSManRequestFailure() 84 # TODO(ifarkas): enable cert verification
85 verify=False)
86 break
87 except (requests.exceptions.ConnectionError,
88 requests.exceptions.SSLError) as ex:
89
90 error_msg = "A {error_type} error occurred while " \
91 " communicating with {host}, attempt {num_tries} of " \
92 "{retries}".format(
93 error_type=type(ex).__name__,
94 host=self.host,
95 num_tries=num_tries,
96 retries=self.retries)
97
98 if num_tries == self.retries:
99 LOG.error(error_msg)
100 raise exceptions.WSManRequestFailure(
101 "A {error_type} error occurred while communicating "
102 "with {host}: {error}".format(
103 error_type=type(ex).__name__,
104 host=self.host,
105 error=ex))
106 else:
107 LOG.warning(error_msg)
108
109 num_tries += 1
110 if self.retry_delay > 0 and num_tries <= self.retries:
111 time.sleep(self.retry_delay)
112
113 except requests.exceptions.RequestException as ex:
114 error_msg = "A {error_type} error occurred while " \
115 "communicating with {host}: {error}".format(
116 error_type=type(ex).__name__,
117 host=self.host,
118 error=ex)
119 LOG.error(error_msg)
120 raise exceptions.WSManRequestFailure(error_msg)
71 121
72 LOG.debug('Received response from %(endpoint)s: %(payload)s', 122 LOG.debug('Received response from %(endpoint)s: %(payload)s',
73 {'endpoint': self.endpoint, 'payload': resp.content}) 123 {'endpoint': self.endpoint, 'payload': resp.content})