Add tests, improve connection handling
- improve logging during connection handling - add testing of the connection handler
This commit is contained in:
@@ -115,19 +115,23 @@ Clients should always be prepared for:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import ssl
|
|
||||||
import urllib2
|
|
||||||
from urlparse import urlparse
|
|
||||||
import httplib
|
|
||||||
import base64
|
import base64
|
||||||
import json
|
|
||||||
import hashlib
|
|
||||||
import gzip
|
import gzip
|
||||||
|
import hashlib
|
||||||
|
import httplib
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import ssl
|
||||||
import StringIO
|
import StringIO
|
||||||
import sys
|
import sys
|
||||||
|
import urllib2
|
||||||
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
LOG.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
class RedfishConnection(object):
|
class RedfishConnection(object):
|
||||||
"""Implements basic connection handling for Redfish APIs."""
|
"""Implements basic connection handling for Redfish APIs."""
|
||||||
|
|
||||||
@@ -138,12 +142,14 @@ class RedfishConnection(object):
|
|||||||
self.user_name = user_name
|
self.user_name = user_name
|
||||||
self.password = password
|
self.password = password
|
||||||
self.auth_token = auth_token
|
self.auth_token = auth_token
|
||||||
self.enforse_SSL = enforse_SSL
|
self.enforce_SSL = enforce_SSL
|
||||||
|
|
||||||
# TODO: cache the token returned by this call
|
# TODO: cache the token returned by this call
|
||||||
auth_dict = {'Password': self.password, 'UserName': self.user_name}
|
auth_dict = {'Password': self.password, 'UserName': self.user_name}
|
||||||
self.rest_post(self.host, '/rest/v1/sessions', None,
|
self.rest_post('/rest/v1/sessions', None, json.dumps(auth_dict))
|
||||||
json.dumps(auth_dict), self.user_name, self.password)
|
|
||||||
# TODO: do some schema discovery here and cache the result
|
# TODO: do some schema discovery here and cache the result
|
||||||
|
LOG.debug('Connection established to host %s.', self.host)
|
||||||
|
|
||||||
def _op(self, operation, suburi, request_headers=None, request_body=None):
|
def _op(self, operation, suburi, request_headers=None, request_body=None):
|
||||||
"""
|
"""
|
||||||
@@ -155,7 +161,10 @@ class RedfishConnection(object):
|
|||||||
:param request_body: optional JSON body
|
:param request_body: optional JSON body
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url = urlparse('https://' + self.host + suburi)
|
# If the http schema wasn't specified, default to HTTPS
|
||||||
|
if self.host[0:4] != 'http':
|
||||||
|
self.host = 'https://' + self.host
|
||||||
|
url = urlparse(self.host + suburi)
|
||||||
|
|
||||||
if not isinstance(request_headers, dict): request_headers = dict()
|
if not isinstance(request_headers, dict): request_headers = dict()
|
||||||
|
|
||||||
@@ -178,7 +187,7 @@ class RedfishConnection(object):
|
|||||||
if( sys.version_info.major == 2 and
|
if( sys.version_info.major == 2 and
|
||||||
sys.version_info.minor == 7 and
|
sys.version_info.minor == 7 and
|
||||||
sys.version_info.micro >= 9 and
|
sys.version_info.micro >= 9 and
|
||||||
enforce_SSL == False):
|
self.enforce_SSL == False):
|
||||||
cont=ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
cont=ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
cont.verify_mode = ssl.CERT_NONE
|
cont.verify_mode = ssl.CERT_NONE
|
||||||
conn = httplib.HTTPSConnection(host=url.netloc, strict=True, context=cont)
|
conn = httplib.HTTPSConnection(host=url.netloc, strict=True, context=cont)
|
||||||
@@ -187,10 +196,7 @@ class RedfishConnection(object):
|
|||||||
elif url.scheme == 'http':
|
elif url.scheme == 'http':
|
||||||
conn = httplib.HTTPConnection(host=url.netloc, strict=True)
|
conn = httplib.HTTPConnection(host=url.netloc, strict=True)
|
||||||
else:
|
else:
|
||||||
assert(False)
|
raise RedfishException(message='Unknown connection schema')
|
||||||
conn.request(operation, url.path, headers=request_headers, body=json.dumps(request_body))
|
|
||||||
resp = conn.getresponse()
|
|
||||||
body = resp.read()
|
|
||||||
|
|
||||||
# NOTE: Do not assume every HTTP operation will return a JSON body.
|
# NOTE: Do not assume every HTTP operation will return a JSON body.
|
||||||
# For example, ExtendedError structures are only required for HTTP 400
|
# For example, ExtendedError structures are only required for HTTP 400
|
||||||
@@ -198,6 +204,9 @@ class RedfishConnection(object):
|
|||||||
# of the other HTTP status code. In particular, 200 OK responses
|
# of the other HTTP status code. In particular, 200 OK responses
|
||||||
# should not have to return any body.
|
# should not have to return any body.
|
||||||
|
|
||||||
|
conn.request(operation, url.path, headers=request_headers, body=json.dumps(request_body))
|
||||||
|
resp = conn.getresponse()
|
||||||
|
body = resp.read()
|
||||||
# NOTE: this makes sure the headers names are all lower cases because
|
# NOTE: this makes sure the headers names are all lower cases because
|
||||||
# HTTP says they are case insensitive
|
# HTTP says they are case insensitive
|
||||||
headers = dict((x.lower(), y) for x, y in resp.getheaders())
|
headers = dict((x.lower(), y) for x, y in resp.getheaders())
|
||||||
|
|||||||
@@ -19,10 +19,75 @@ test_redfish
|
|||||||
Tests for `redfish` module.
|
Tests for `redfish` module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import fixtures
|
||||||
|
import httplib
|
||||||
|
import mock
|
||||||
|
import ssl
|
||||||
|
|
||||||
from redfish.tests import base
|
from redfish.tests import base
|
||||||
|
from redfish import connection
|
||||||
|
|
||||||
|
|
||||||
class TestRedfish(base.TestCase):
|
def get_fake_params(host=None, user=None, pword=None):
|
||||||
|
if not host:
|
||||||
|
host = 'https://127.0.0.1'
|
||||||
|
if not user:
|
||||||
|
user = 'admin'
|
||||||
|
if not pword:
|
||||||
|
pword = 'password'
|
||||||
|
return (host, user, pword)
|
||||||
|
|
||||||
def test_something(self):
|
|
||||||
pass
|
def get_response():
|
||||||
|
class _response(object):
|
||||||
|
status = 200
|
||||||
|
def read(self):
|
||||||
|
return "{'foo': 'bar'}"
|
||||||
|
def getheaders(self):
|
||||||
|
return [('Fake-Header', 'fake value')]
|
||||||
|
return _response()
|
||||||
|
|
||||||
|
|
||||||
|
class TestException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestRedfishConnection(base.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestRedfishConnection, self).setUp()
|
||||||
|
self.log_fixture = self.useFixture(fixtures.FakeLogger())
|
||||||
|
self.con_mock = mock.MagicMock()
|
||||||
|
self.con_mock.getresponse = get_response
|
||||||
|
|
||||||
|
self.http_mock = mock.patch.object(httplib, 'HTTPConnection').start()
|
||||||
|
self.http_mock.return_value = self.con_mock
|
||||||
|
self.https_mock = mock.patch.object(httplib, 'HTTPSConnection').start()
|
||||||
|
self.https_mock.return_value = self.con_mock
|
||||||
|
self.addCleanup(self.http_mock.stop)
|
||||||
|
self.addCleanup(self.https_mock.stop)
|
||||||
|
|
||||||
|
def test_create_ok(self):
|
||||||
|
con = connection.RedfishConnection(*get_fake_params())
|
||||||
|
self.assertEqual(1, self.https_mock.call_count)
|
||||||
|
self.assertEqual(0, self.http_mock.call_count)
|
||||||
|
|
||||||
|
def test_create_calls_https_connect(self):
|
||||||
|
self.https_mock.side_effect = TestException()
|
||||||
|
self.assertRaises(TestException,
|
||||||
|
connection.RedfishConnection,
|
||||||
|
*get_fake_params(host='https://fake'))
|
||||||
|
|
||||||
|
def test_create_calls_http_connect(self):
|
||||||
|
self.http_mock.side_effect = TestException()
|
||||||
|
self.assertRaises(TestException,
|
||||||
|
connection.RedfishConnection,
|
||||||
|
*get_fake_params(host='http://fake'))
|
||||||
|
|
||||||
|
# FIXME: ssl module has no attribute 'SSLContext'
|
||||||
|
# NOTE: skip this test if sys.version_info (major, minor) != (2, 7) and micro < 9
|
||||||
|
# @mock.patch.object(ssl, 'SSLContext')
|
||||||
|
# def test_insecure_ssl(self, ssl_mock):
|
||||||
|
# ssl_mock.return_value = mock.Mock()
|
||||||
|
# con = connection.RedfishConnection(*get_fake_params)
|
||||||
|
# ssl_mock.assert_called_once_with(ssl.PROTOCOL_TLSv1)
|
||||||
|
|||||||
Reference in New Issue
Block a user