QNAP Drivers - Move from httplib to requests

Use driver_ssl_cert_verify under backend section to
enable or disable SSL verfication.

NOTE: IPv6 isn't supported by QNAP driver.

Change-Id: Iba886fd0bd401052a444eb7a4427607e693d7c81
Closes-Bug: 1658766
Partial-Bug: 1188189
This commit is contained in:
Ibadulla Khan 2018-01-26 19:08:35 +05:30 committed by Ibad Khan
parent baabd09f63
commit 431b4284bf
2 changed files with 1074 additions and 1206 deletions

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,6 @@ from collections import OrderedDict
import eventlet
import functools
import re
import ssl
import threading
import time
@ -32,8 +31,8 @@ from oslo_log import log as logging
from oslo_utils import strutils
from oslo_utils import timeutils
from oslo_utils import units
import requests
import six
from six.moves import http_client
from six.moves import urllib
from cinder import exception
@ -47,12 +46,13 @@ LOG = logging.getLogger(__name__)
qnap_opts = [
cfg.URIOpt('qnap_management_url',
help='The URL to management QNAP Storage'),
help='The URL to management QNAP Storage. '
'Driver does not support IPv6 address in URL.'),
cfg.StrOpt('qnap_poolname',
help='The pool name in the QNAP Storage'),
cfg.StrOpt('qnap_storage_protocol',
default='iscsi',
help='Communication protocol to access QNAP storage'),
help='Communication protocol to access QNAP storage')
]
CONF = cfg.CONF
@ -74,6 +74,8 @@ class QnapISCSIDriver(san.SanISCSIDriver):
1.2.002:
Add support for QES fw 2.0.0.
NOTE: Set driver_ssl_cert_verify as True under backend section to
enable SSL verification.
"""
# ThirdPartySystems wiki page
@ -152,7 +154,8 @@ class QnapISCSIDriver(san.SanISCSIDriver):
self.api_executor = QnapAPIExecutor(
username=self.configuration.san_login,
password=self.configuration.san_password,
management_url=self.configuration.qnap_management_url)
management_url=self.configuration.qnap_management_url,
verify_ssl=self.configuration.driver_ssl_cert_verify)
nas_model_name, internal_model_name, fw_version = (
self.api_executor.get_basic_info(
@ -190,7 +193,8 @@ class QnapISCSIDriver(san.SanISCSIDriver):
return (QnapAPIExecutorTS(
username=self.configuration.san_login,
password=self.configuration.san_password,
management_url=self.configuration.qnap_management_url))
management_url=self.configuration.qnap_management_url,
verify_ssl=self.configuration.driver_ssl_cert_verify))
elif model_type in tes_model_types:
if 'TS' in internal_model_name:
if (fw_version >= "4.2") and (fw_version <= "4.4"):
@ -202,20 +206,23 @@ class QnapISCSIDriver(san.SanISCSIDriver):
return (QnapAPIExecutorTS(
username=self.configuration.san_login,
password=self.configuration.san_password,
management_url=self.configuration.qnap_management_url))
management_url=self.configuration.qnap_management_url,
verify_ssl=self.configuration.driver_ssl_cert_verify))
elif "1.1.2" <= fw_version <= "2.0.9999":
LOG.debug('Create TES API Executor')
return (QnapAPIExecutorTES(
username=self.configuration.san_login,
password=self.configuration.san_password,
management_url=self.configuration.qnap_management_url))
management_url=self.configuration.qnap_management_url,
verify_ssl=self.configuration.driver_ssl_cert_verify))
elif model_type in es_model_types:
if "1.1.2" <= fw_version <= "2.0.9999":
LOG.debug('Create ES API Executor')
return (QnapAPIExecutor(
username=self.configuration.san_login,
password=self.configuration.san_password,
management_url=self.configuration.qnap_management_url))
management_url=self.configuration.qnap_management_url,
verify_ssl=self.configuration.driver_ssl_cert_verify))
msg = _('Model not support')
raise exception.VolumeDriverException(message=msg)
@ -1119,9 +1126,15 @@ class QnapAPIExecutor(object):
self.password = kwargs['password']
self.ip, self.port, self.ssl = (
self._parse_management_url(kwargs['management_url']))
self.verify_ssl = kwargs['verify_ssl']
self._login()
def _parse_management_url(self, management_url):
# NOTE(Ibad): This parser isn't compatible with IPv6 address.
# Typical IPv6 address will have : as delimiters and
# URL is represented as https://[3ffe:2a00:100:7031::1]:8080
# since the regular expression below uses : to identify ip and port
# it won't work with IPv6 address.
pattern = re.compile(r"(http|https)\:\/\/(\S+)\:(\d+)")
matches = pattern.match(management_url)
if matches.group(1) == 'http':
@ -1136,23 +1149,10 @@ class QnapAPIExecutor(object):
"""Get the basic information of NAS."""
management_ip, management_port, management_ssl = (
self._parse_management_url(management_url))
connection = None
if management_ssl:
if hasattr(ssl, '_create_unverified_context'):
context = ssl._create_unverified_context()
connection = http_client.HTTPSConnection(management_ip,
port=management_port,
context=context)
else:
connection = http_client.HTTPSConnection(management_ip,
port=management_port)
else:
connection = (
http_client.HTTPConnection(management_ip, management_port))
connection.request('GET', '/cgi-bin/authLogin.cgi')
response = connection.getresponse()
data = response.read()
response = self._get_response(management_ip, management_port,
management_ssl, '/cgi-bin/authLogin.cgi')
data = response.text
root = ET.fromstring(data)
@ -1162,6 +1162,29 @@ class QnapAPIExecutor(object):
return nas_model_name, internal_model_name, fw_version
def _get_response(self, host_ip, host_port, use_ssl, action, body=None):
""""Execute http request and return response."""
method = 'GET'
headers = None
protocol = 'https' if use_ssl else 'http'
verify = self.verify_ssl if use_ssl else False
# NOTE(ibad): URL formed here isn't IPv6 compatible
# we should surround host ip with [] when IPv6 is supported
# so the final URL can be like https://[3ffe:2a00:100:7031::1]:8080
url = '%s://%s:%s%s' % (protocol, host_ip, host_port, action)
if body:
method = 'POST'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'charset': 'utf-8'
}
response = requests.request(method, url, data=body, headers=headers,
verify=verify)
return response
def _execute_and_get_response_details(self, nas_ip, url, post_parm=None):
"""Will prepare response after executing an http request."""
LOG.debug('_execute_and_get_response_details url: %s', url)
@ -1169,53 +1192,23 @@ class QnapAPIExecutor(object):
res_details = {}
start_time1 = time.time()
# Prepare the connection
if self.ssl:
if hasattr(ssl, '_create_unverified_context'):
context = ssl._create_unverified_context()
connection = http_client.HTTPSConnection(nas_ip,
port=self.port,
context=context)
else:
connection = http_client.HTTPSConnection(
nas_ip, port=self.port)
else:
connection = http_client.HTTPConnection(nas_ip, self.port)
elapsed_time1 = time.time() - start_time1
LOG.debug('connection elapsed_time: %s', elapsed_time1)
start_time2 = time.time()
# Make the connection
if post_parm is None:
connection.request('GET', url)
else:
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"charset": "utf-8"}
connection.request('POST', url, post_parm, headers)
start_time2 = time.time()
response = self._get_response(
nas_ip, self.port, self.ssl, url, post_parm)
elapsed_time2 = time.time() - start_time2
LOG.debug('request elapsed_time: %s', elapsed_time2)
# Extract the response as the connection was successful
start_time = time.time()
response = connection.getresponse()
elapsed_time = time.time() - start_time
LOG.debug('cgi elapsed_time: %s', elapsed_time)
# Read the response
data = response.read()
LOG.debug('response status: %s', response.status)
data = response.text
LOG.debug('response status: %s', response.status_code)
# Extract http error msg if any
error_details = None
res_details['data'] = data
res_details['error'] = error_details
res_details['http_status'] = response.status
res_details['http_status'] = response.status_code
connection.close()
return res_details
def execute_login(self):