# Certificate file
# cert_file =
+# Disable SSL certificate verification
+# insecure_ssl = false
+
# Maximum attempts per OFC API request. NEC plugin retries
# API request to OFC when OFC returns ServiceUnavailable (503).
# The value must be greater than 0.
help=_("Key file")),
cfg.StrOpt('cert_file', default=None,
help=_("Certificate file")),
+ cfg.BoolOpt('insecure_ssl', default=False,
+ help=_("Disable SSL certificate verification")),
cfg.IntOpt('api_max_attempts', default=3,
help=_("Maximum attempts per OFC API request."
"NEC plugin retries API request to OFC "
# under the License.
# @author: Ryota MIBU
-import httplib
import json
-import socket
import time
+import requests
+
from neutron.openstack.common import excutils
from neutron.openstack.common import log as logging
from neutron.plugins.nec.common import config
"""A HTTP/HTTPS client for OFC Drivers."""
def __init__(self, host="127.0.0.1", port=8888, use_ssl=False,
- key_file=None, cert_file=None):
+ key_file=None, cert_file=None, insecure_ssl=False):
"""Creates a new client to some OFC.
:param host: The host where service resides
:param use_ssl: True to use SSL, False to use HTTP
:param key_file: The SSL key file to use if use_ssl is true
:param cert_file: The SSL cert file to use if use_ssl is true
+ :param insecure_ssl: Don't verify SSL certificate
"""
self.host = host
self.port = port
self.use_ssl = use_ssl
self.key_file = key_file
self.cert_file = cert_file
+ self.insecure_ssl = insecure_ssl
self.connection = None
- def get_connection(self):
- """Returns the proper connection."""
- if self.use_ssl:
- connection_type = httplib.HTTPSConnection
- else:
- connection_type = httplib.HTTPConnection
-
- # Open connection and send request, handling SSL certs
- certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
- certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
- if self.use_ssl and len(certs):
- conn = connection_type(self.host, self.port, **certs)
- else:
- conn = connection_type(self.host, self.port)
- return conn
-
def _format_error_message(self, status, detail):
detail = ' ' + detail if detail else ''
return (_("Operation on OFC failed: %(status)s%(msg)s") %
{'status': status, 'msg': detail})
+ def _get_response(self, method, action, body=None):
+ headers = {"Content-Type": "application/json"}
+ protocol = "http"
+ certs = {'key_file': self.key_file, 'cert_file': self.cert_file}
+ certs = dict((x, certs[x]) for x in certs if certs[x] is not None)
+ verify = True
+
+ if self.use_ssl:
+ protocol = "https"
+ if self.insecure_ssl:
+ verify = False
+
+ url = "%s://%s:%d%s" % (protocol, self.host, int(self.port),
+ action)
+
+ res = requests.request(method, url, data=body, headers=headers,
+ cert=certs, verify=verify)
+ return res
+
def do_single_request(self, method, action, body=None):
action = config.OFC.path_prefix + action
LOG.debug(_("Client request: %(host)s:%(port)s "
if type(body) is dict:
body = json.dumps(body)
try:
- conn = self.get_connection()
- headers = {"Content-Type": "application/json"}
- conn.request(method, action, body, headers)
- res = conn.getresponse()
- data = res.read()
+ res = self._get_response(method, action, body)
+ data = res.text
LOG.debug(_("OFC returns [%(status)s:%(data)s]"),
- {'status': res.status,
+ {'status': res.status_code,
'data': data})
# Try to decode JSON data if possible.
except (ValueError, TypeError):
pass
- if res.status in (httplib.OK,
- httplib.CREATED,
- httplib.ACCEPTED,
- httplib.NO_CONTENT):
+ if res.status_code in (requests.codes.OK,
+ requests.codes.CREATED,
+ requests.codes.ACCEPTED,
+ requests.codes.NO_CONTENT):
return data
- elif res.status == httplib.SERVICE_UNAVAILABLE:
- retry_after = res.getheader('retry-after')
+ elif res.status_code == requests.codes.SERVICE_UNAVAILABLE:
+ retry_after = res.headers.get('retry-after')
LOG.warning(_("OFC returns ServiceUnavailable "
"(retry-after=%s)"), retry_after)
raise nexc.OFCServiceUnavailable(retry_after=retry_after)
- elif res.status == httplib.NOT_FOUND:
+ elif res.status_code == requests.codes.NOT_FOUND:
LOG.info(_("Specified resource %s does not exist on OFC "),
action)
raise nexc.OFCResourceNotFound(resource=action)
else:
LOG.warning(_("Operation on OFC failed: "
"status=%(status)s, detail=%(detail)s"),
- {'status': res.status, 'detail': data})
+ {'status': res.status_code, 'detail': data})
params = {'reason': _("Operation on OFC failed"),
- 'status': res.status}
+ 'status': res.status_code}
if isinstance(data, dict):
params['err_code'] = data.get('err_code')
params['err_msg'] = data.get('err_msg')
else:
params['err_msg'] = data
raise nexc.OFCException(**params)
- except (socket.error, IOError) as e:
+ except requests.exceptions.RequestException as e:
reason = _("Failed to connect OFC : %s") % e
LOG.error(reason)
raise nexc.OFCException(reason=reason)
port=conf_ofc.port,
use_ssl=conf_ofc.use_ssl,
key_file=conf_ofc.key_file,
- cert_file=conf_ofc.cert_file)
+ cert_file=conf_ofc.cert_file,
+ insecure_ssl=conf_ofc.insecure_ssl)
@classmethod
def filter_supported(cls):
# @author: Akihiro Motoki
import json
-import socket
import mock
from oslo.config import cfg
+import requests
from neutron.plugins.nec.common import config
from neutron.plugins.nec.common import exceptions as nexc
from neutron.tests import base
+class FakeResponse(requests.Response):
+ def __init__(self, status_code=None, text=None, headers=None):
+ self._text = text
+ self.status_code = status_code
+ if headers is not None:
+ self.headers = headers
+
+ @property
+ def text(self):
+ return self._text
+
+
class OFCClientTest(base.BaseTestCase):
def _test_do_request(self, status, resbody, expected_data, exctype=None,
exc_checks=None, path_prefix=None):
- res = mock.Mock()
- res.status = status
- res.read.return_value = resbody
-
- conn = mock.Mock()
- conn.getresponse.return_value = res
+ req = mock.Mock(return_value=(FakeResponse(status, resbody)))
- with mock.patch.object(ofc_client.OFCClient, 'get_connection',
- return_value=conn):
+ with mock.patch.object(requests, 'request', req):
client = ofc_client.OFCClient()
path = '/somewhere'
realpath = path_prefix + path if path_prefix else path
self.assertEqual(response, expected_data)
headers = {"Content-Type": "application/json"}
- expected = [
- mock.call.request('GET', realpath, '{}', headers),
- mock.call.getresponse(),
- ]
- conn.assert_has_calls(expected)
+ req.assert_called_with('GET', 'http://127.0.0.1:8888' + realpath,
+ verify=True, cert={}, data='{}',
+ headers=headers)
def test_do_request_200_json_value(self):
self._test_do_request(200, json.dumps([1, 2, 3]), [1, 2, 3])
exc_checks)
def test_do_request_socket_error(self):
- conn = mock.Mock()
- conn.request.side_effect = socket.error
-
data = _("An OFC exception has occurred: Failed to connect OFC : ")
- with mock.patch.object(ofc_client.OFCClient, 'get_connection',
- return_value=conn):
+ req = mock.Mock()
+ req.side_effect = requests.exceptions.RequestException
+
+ with mock.patch.object(requests, 'request', req):
client = ofc_client.OFCClient()
e = self.assertRaises(nexc.OFCException, client.do_request,
self.assertIsNone(getattr(e, k))
headers = {"Content-Type": "application/json"}
- expected = [
- mock.call.request('GET', '/somewhere', '{}', headers),
- ]
- conn.assert_has_calls(expected)
+ req.assert_called_with('GET', 'http://127.0.0.1:8888/somewhere',
+ verify=True, cert={}, data='{}',
+ headers=headers)
def test_do_request_retry_fail_after_one_attempts(self):
self._test_do_request_retry_after(1, api_max_attempts=1)
cfg.CONF.set_override('api_max_attempts', api_max_attempts,
group='OFC')
- res_unavail = mock.Mock()
- res_unavail.status = 503
- res_unavail.read.return_value = None
- res_unavail.getheader.return_value = '10'
+ res_unavail = FakeResponse(503, headers={'retry-after': '10'})
+ res_ok = FakeResponse(200)
- res_ok = mock.Mock()
- res_ok.status = 200
- res_ok.read.return_value = None
-
- conn = mock.Mock()
+ req = mock.Mock()
if succeed_final:
- side_effect = [res_unavail] * (exp_request_count - 1) + [res_ok]
+ req.side_effect = ([res_unavail] * (exp_request_count - 1)
+ + [res_ok])
else:
- side_effect = [res_unavail] * exp_request_count
- conn.getresponse.side_effect = side_effect
+ req.side_effect = [res_unavail] * exp_request_count
- with mock.patch.object(ofc_client.OFCClient, 'get_connection',
- return_value=conn):
+ with mock.patch.object(requests, 'request', req):
with mock.patch('time.sleep') as sleep:
client = ofc_client.OFCClient()
if succeed_final:
client.do_request,
'GET', '/somewhere')
self.assertEqual('10', e.retry_after)
+
headers = {"Content-Type": "application/json"}
- expected = [
- mock.call.request('GET', '/somewhere', None, headers),
- mock.call.getresponse(),
- ] * exp_request_count
- conn.assert_has_calls(expected)
- self.assertEqual(exp_request_count, conn.request.call_count)
+ req.assert_called_with('GET', 'http://127.0.0.1:8888/somewhere',
+ verify=True, cert={}, data=None,
+ headers=headers)
+ self.assertEqual(exp_request_count, req.call_count)
self.assertEqual(exp_request_count - 1, sleep.call_count)
use_ssl = False
key_file = None
cert_file = None
+ insecure_ssl = False
def _ofc(id):