[NetApp] Fix HTTPS connection for python 3.7

With python3.7, the eventlet is breaking the ssl.py, so the https
is not working. This patch fixes it by changing the request library
(urllib by requests), the new library can be built over the
pyopenssl.py instead of ssl.py.

Closes-Bug: #1878993
Change-Id: I9c0b1f332ead25634f3dc3aebfdc8b51dfbc4178
This commit is contained in:
Felipe Rodrigues 2020-05-06 23:54:59 +00:00
parent 1f7982e7f3
commit 29622725e4
4 changed files with 58 additions and 50 deletions

View File

@ -23,8 +23,9 @@ import re
from lxml import etree
from oslo_log import log
import requests
from requests import auth
import six
from six.moves import urllib
from manila import exception
from manila.i18n import _
@ -61,6 +62,7 @@ class NaServer(object):
TRANSPORT_TYPE_HTTP = 'http'
TRANSPORT_TYPE_HTTPS = 'https'
SSL_CERT_DEFAULT = "/etc/ssl/certs/"
SERVER_TYPE_FILER = 'filer'
SERVER_TYPE_DFM = 'dfm'
URL_FILER = 'servlets/netapp.servlets.admin.XMLrequest_filer'
@ -232,8 +234,8 @@ class NaServer(object):
"""Invoke the API on the server."""
if na_element and not isinstance(na_element, NaElement):
ValueError('NaElement must be supplied to invoke API')
request, request_element = self._create_request(na_element,
enable_tunneling)
request_element = self._create_request(na_element, enable_tunneling)
request_d = request_element.to_string()
api_name = na_element.get_name()
api_name_matches_regex = (re.match(self._api_trace_pattern, api_name)
@ -242,23 +244,26 @@ class NaServer(object):
if self._trace and api_name_matches_regex:
LOG.debug("Request: %s", request_element.to_string(pretty=True))
if (not hasattr(self, '_opener') or not self._opener
if (not hasattr(self, '_session') or not self._session
or self._refresh_conn):
self._build_opener()
self._build_session()
try:
if hasattr(self, '_timeout'):
response = self._opener.open(request, timeout=self._timeout)
response = self._session.post(
self._get_url(), data=request_d, timeout=self._timeout)
else:
response = self._opener.open(request)
except urllib.error.HTTPError as e:
raise NaApiError(e.code, e.msg)
except urllib.error.URLError as e:
response = self._session.post(
self._get_url(), data=request_d)
except requests.HTTPError as e:
raise NaApiError(e.errno, e.strerror)
except requests.URLRequired as e:
raise exception.StorageCommunicationException(six.text_type(e))
except Exception as e:
raise NaApiError(message=e)
response_xml = response.read()
response_element = self._get_result(response_xml)
response_xml = response.text
response_element = self._get_result(
bytes(bytearray(response_xml, encoding='utf-8')))
if self._trace and api_name_matches_regex:
LOG.debug("Response: %s", response_element.to_string(pretty=True))
@ -296,11 +301,7 @@ class NaServer(object):
if enable_tunneling:
self._enable_tunnel_request(netapp_elem)
netapp_elem.add_child_elem(na_element)
request_d = netapp_elem.to_string()
request = urllib.request.Request(
self._get_url(), data=request_d,
headers={'Content-Type': 'text/xml', 'charset': 'utf-8'})
return request, netapp_elem
return netapp_elem
def _enable_tunnel_request(self, netapp_elem):
"""Enables vserver or vfiler tunneling."""
@ -341,20 +342,20 @@ class NaServer(object):
host = '[%s]' % host
return '%s://%s:%s/%s' % (self._protocol, host, self._port, self._url)
def _build_opener(self):
def _build_session(self):
if self._auth_style == NaServer.STYLE_LOGIN_PASSWORD:
auth_handler = self._create_basic_auth_handler()
else:
auth_handler = self._create_certificate_auth_handler()
opener = urllib.request.build_opener(auth_handler)
self._opener = opener
self._session = requests.Session()
self._session.auth = auth_handler
self._session.verify = NaServer.SSL_CERT_DEFAULT
self._session.headers = {
'Content-Type': 'text/xml', 'charset': 'utf-8'}
def _create_basic_auth_handler(self):
password_man = urllib.request.HTTPPasswordMgrWithDefaultRealm()
password_man.add_password(None, self._get_url(), self._username,
self._password)
auth_handler = urllib.request.HTTPBasicAuthHandler(password_man)
return auth_handler
return auth.HTTPBasicAuth(self._username, self._password)
def _create_certificate_auth_handler(self):
raise NotImplementedError()

View File

@ -15,7 +15,7 @@
from unittest import mock
from lxml import etree
from six.moves import urllib
import requests
from manila.share.drivers.netapp.dataontap.client import api
@ -2638,7 +2638,7 @@ FAKE_RESULT_API_ERRNO_VALID.add_attr('errno', '14956')
FAKE_RESULT_SUCCESS = api.NaElement('result')
FAKE_RESULT_SUCCESS.add_attr('status', 'passed')
FAKE_HTTP_OPENER = urllib.request.build_opener()
FAKE_HTTP_SESSION = requests.Session()
FAKE_MANAGE_VOLUME = {
'aggregate': SHARE_AGGREGATE_NAME,

View File

@ -22,7 +22,7 @@ Tests for NetApp API layer
from unittest import mock
import ddt
from six.moves import urllib
import requests
from manila import exception
from manila.share.drivers.netapp.dataontap.client import api
@ -190,14 +190,12 @@ class NetAppApiServerTests(test.TestCase):
"""Tests handling of HTTPError"""
na_element = fake.FAKE_NA_ELEMENT
self.mock_object(self.root, '_create_request', mock.Mock(
return_value=('abc', fake.FAKE_NA_ELEMENT)))
return_value=fake.FAKE_NA_ELEMENT))
self.mock_object(api, 'LOG')
self.root._opener = fake.FAKE_HTTP_OPENER
self.mock_object(self.root, '_build_opener')
self.mock_object(self.root._opener, 'open', mock.Mock(
side_effect=urllib.error.HTTPError(url='', hdrs='',
fp=None, code='401',
msg='httperror')))
self.root._session = fake.FAKE_HTTP_SESSION
self.mock_object(self.root, '_build_session')
self.mock_object(self.root._session, 'post', mock.Mock(
side_effect=requests.HTTPError()))
self.assertRaises(api.NaApiError, self.root.invoke_elem,
na_element)
@ -206,12 +204,12 @@ class NetAppApiServerTests(test.TestCase):
"""Tests handling of URLError"""
na_element = fake.FAKE_NA_ELEMENT
self.mock_object(self.root, '_create_request', mock.Mock(
return_value=('abc', fake.FAKE_NA_ELEMENT)))
return_value=fake.FAKE_NA_ELEMENT))
self.mock_object(api, 'LOG')
self.root._opener = fake.FAKE_HTTP_OPENER
self.mock_object(self.root, '_build_opener')
self.mock_object(self.root._opener, 'open', mock.Mock(
side_effect=urllib.error.URLError(reason='urlerror')))
self.root._session = fake.FAKE_HTTP_SESSION
self.mock_object(self.root, '_build_session')
self.mock_object(self.root._session, 'post', mock.Mock(
side_effect=requests.URLRequired()))
self.assertRaises(exception.StorageCommunicationException,
self.root.invoke_elem,
@ -221,11 +219,11 @@ class NetAppApiServerTests(test.TestCase):
"""Tests handling of Unknown Exception"""
na_element = fake.FAKE_NA_ELEMENT
self.mock_object(self.root, '_create_request', mock.Mock(
return_value=('abc', fake.FAKE_NA_ELEMENT)))
return_value=fake.FAKE_NA_ELEMENT))
self.mock_object(api, 'LOG')
self.root._opener = fake.FAKE_HTTP_OPENER
self.mock_object(self.root, '_build_opener')
self.mock_object(self.root._opener, 'open', mock.Mock(
self.root._session = fake.FAKE_HTTP_SESSION
self.mock_object(self.root, '_build_session')
self.mock_object(self.root._session, 'post', mock.Mock(
side_effect=Exception))
exception = self.assertRaises(api.NaApiError, self.root.invoke_elem,
@ -247,15 +245,18 @@ class NetAppApiServerTests(test.TestCase):
self.root._trace = trace_enabled
self.root._api_trace_pattern = trace_pattern
self.mock_object(self.root, '_create_request', mock.Mock(
return_value=('abc', fake.FAKE_NA_ELEMENT)))
return_value=fake.FAKE_NA_ELEMENT))
self.mock_object(api, 'LOG')
self.root._opener = fake.FAKE_HTTP_OPENER
self.mock_object(self.root, '_build_opener')
self.root._session = fake.FAKE_HTTP_SESSION
self.mock_object(self.root, '_build_session')
self.mock_object(self.root, '_get_result', mock.Mock(
return_value=fake.FAKE_NA_ELEMENT))
opener_mock = self.mock_object(
self.root._opener, 'open', mock.Mock())
opener_mock.read.side_effect = ['resp1', 'resp2']
response = mock.Mock()
response.text = 'res1'
self.mock_object(
self.root._session, 'post', mock.Mock(
return_value=response))
self.root.invoke_elem(na_element)

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixed `bug #1878993 <https://bugs.launchpad.net/manila/+bug/1878993>`_
that caused a failure on HTTPS connections within NetApp backend using
python 3.7.