Huawei manila driver support multi RestURLs

- Add support for multi RestURLs
- Add multi RestURLs unit tests

RestURL is an access address of the REST interface.
Multi RestURLs can be configured in manila_huawei_conf.xml(separated by ";")
When one of the RestURL failed to connect, driver will retry another automatically.

Implements: blueprint huawei-manila-driver-support-multi-resturls
Change-Id: Iaecdd4761e388255af833a3e5b0bd1e1d42a0e22
This commit is contained in:
zhaohua 2015-07-02 11:43:20 +08:00
parent 2e4a34a331
commit d2abb50840
3 changed files with 115 additions and 23 deletions

View File

@ -26,3 +26,6 @@ ACCESS_NFS_RW = "1"
ACCESS_NFS_RO = "0"
ACCESS_CIFS_RW = "5"
ACCESS_CIFS_RO = "0"
ERROR_CONNECT_TO_SERVER = -403
ERROR_UNAUTHORIZED_TO_SERVER = -401

View File

@ -43,7 +43,7 @@ class RestHelper(object):
"Content-Type": "application/json",
}
def call(self, url, data=None, method=None):
def do_call(self, url, data=None, method=None):
"""Send requests to server.
Send HTTPS call, get response in JSON.
@ -58,6 +58,7 @@ class RestHelper(object):
'data': data})
opener = urlreq.build_opener(urlreq.HTTPCookieProcessor(self.cookie))
urlreq.install_opener(opener)
result = None
try:
req = urlreq.Request(url, data, self.headers)
@ -69,36 +70,80 @@ class RestHelper(object):
LOG.debug('Response Data: %(res)s.', {'res': res})
except Exception as err:
LOG.error(_LE('Bad response from server: %s.') % err)
raise err
LOG.error(_LE('\nBad response from server: %(url)s.'
' Error: %(err)s'), {'url': url, 'err': err})
res = '{"error":{"code":%s,' \
'"description":"Connect server error"}}' \
% constants.ERROR_CONNECT_TO_SERVER
try:
res_json = jsonutils.loads(res)
result = jsonutils.loads(res)
except Exception as err:
err_msg = (_('JSON transfer error: %s.') % err)
LOG.error(err_msg)
raise exception.InvalidShare(reason=err_msg)
raise exception.InvalidInput(reason=err_msg)
return res_json
return result
def login(self):
"""Log in huawei array."""
"""Login huawei array."""
login_info = self._get_login_info()
url = login_info['RestURL'] + "xx/sessions"
data = jsonutils.dumps({"username": login_info['UserName'],
"password": login_info['UserPassword'],
"scope": "0"})
result = self.call(url, data)
if (result['error']['code'] != 0) or ("data" not in result):
err_msg = (_("Login error, reason is %s.") % result)
urlstr = login_info['RestURL']
url_list = urlstr.split(";")
deviceid = None
for item_url in url_list:
url = item_url.strip('').strip('\n') + "xx/sessions"
data = jsonutils.dumps({"username": login_info['UserName'],
"password": login_info['UserPassword'],
"scope": "0"})
result = self.do_call(url, data)
if((result['error']['code'] != 0)
or ("data" not in result)
or (result['data']['deviceid'] is None)):
err_msg = (_("Login to %s failed, try another") % item_url)
LOG.error(err_msg)
continue
LOG.debug('Login success: %(url)s\n',
{'url': item_url})
deviceid = result['data']['deviceid']
self.url = item_url + deviceid
self.headers['iBaseToken'] = result['data']['iBaseToken']
break
if deviceid is None:
err_msg = (_("All url Login fail"))
LOG.error(err_msg)
raise exception.InvalidShare(reason=err_msg)
deviceid = result['data']['deviceid']
self.url = login_info['RestURL'] + deviceid
self.headers['iBaseToken'] = result['data']['iBaseToken']
return deviceid
def call(self, url, data=None, method=None):
"""Send requests to server.
if fail, try another RestURL
"""
deviceid = None
old_url = self.url
result = self.do_call(url, data, method)
error_code = result['error']['code']
if(error_code == constants.ERROR_CONNECT_TO_SERVER
or error_code == constants.ERROR_UNAUTHORIZED_TO_SERVER):
err_msg = (_("Can't open the recent url, re-login."))
LOG.error(err_msg)
deviceid = self.login()
if deviceid is not None:
LOG.debug('Replace URL: \n'
'Old URL: %(old_url)s\n'
'New URL: %(new_url)s\n',
{'old_url': old_url,
'new_url': self.url})
url = url.replace(old_url, self.url)
result = self.do_call(url, data, method)
return result
def _create_filesystem(self, fs_param):
"""Create file system."""
url = self.url + "/filesystem"

View File

@ -139,15 +139,29 @@ class FakeHuaweiNasHelper(helper.RestHelper):
self.allow_ro_flag = False
self.allow_rw_flag = False
self.extend_share_flag = False
self.test_multi_url_flag = 0
def _change_file_mode(self, filepath):
pass
def call(self, url, data=None, method=None):
def do_call(self, url, data=None, method=None):
url = url.replace('http://100.115.10.69:8082/deviceManager/rest', '')
url = url.replace('/210235G7J20000000000/', '')
if self.test_normal:
if self.test_multi_url_flag == 1:
data = '{"error":{"code":-403}}'
res_json = jsonutils.loads(data)
return res_json
elif self.test_multi_url_flag == 2:
if 'http://100.115.10.70:8082/deviceManager/rest' in url:
url = url.replace('http://100.115.10.70:8082/'
'deviceManager/rest', '')
else:
data = '{"error":{"code":-403}}'
res_json = jsonutils.loads(data)
return res_json
if url == "/xx/sessions" or url == "sessions":
data = data_session(url)
@ -979,11 +993,34 @@ class HuaweiShareDriverTestCase(test.TestCase):
self.driver.delete_snapshot, self._context,
self.cifs_snapshot, self.share_server)
def test_multi_resturls_success(self):
self.recreate_fake_conf_file(multi_url=True)
self.driver.plugin.configuration.manila_huawei_conf_file = (
self.fake_conf_file)
self.driver.plugin.helper.login()
self.driver.plugin.helper.test_multi_url_flag = 2
location = self.driver.create_share(self._context, self.share_nfs,
self.share_server)
self.assertEqual("100.115.10.68:/share_fake_uuid", location)
def test_multi_resturls_fail(self):
self.recreate_fake_conf_file(multi_url=True)
self.driver.plugin.configuration.manila_huawei_conf_file = (
self.fake_conf_file)
self.driver.plugin.helper.login()
self.driver.plugin.helper.test_multi_url_flag = 1
self.assertRaises(exception.InvalidShare,
self.driver.create_share,
self._context,
self.share_nfs,
self.share_server)
def create_fake_conf_file(self, fake_conf_file,
product_flag=True, username_flag=True,
pool_node_flag=True, timeout_flag=True,
wait_interval_flag=True,
alloctype_value='Thick'):
alloctype_value='Thick',
multi_url=False):
doc = xml.dom.minidom.Document()
config = doc.createElement('Config')
doc.appendChild(config)
@ -1019,8 +1056,14 @@ class HuaweiShareDriverTestCase(test.TestCase):
userpassword.appendChild(userpassword_text)
storage.appendChild(userpassword)
url = doc.createElement('RestURL')
url_text = doc.createTextNode('http://100.115.10.69:8082/'
'deviceManager/rest/')
if multi_url:
url_text = doc.createTextNode('http://100.115.10.69:8082/'
'deviceManager/rest/;'
'http://100.115.10.70:8082/'
'deviceManager/rest/')
else:
url_text = doc.createTextNode('http://100.115.10.69:8082/'
'deviceManager/rest/')
url.appendChild(url_text)
storage.appendChild(url)
@ -1070,12 +1113,13 @@ class HuaweiShareDriverTestCase(test.TestCase):
def recreate_fake_conf_file(self, product_flag=True, username_flag=True,
pool_node_flag=True, timeout_flag=True,
wait_interval_flag=True,
alloctype_value='Thick'):
alloctype_value='Thick',
multi_url=False):
self.tmp_dir = tempfile.mkdtemp()
self.fake_conf_file = self.tmp_dir + '/manila_huawei_conf.xml'
self.addCleanup(shutil.rmtree, self.tmp_dir)
self.create_fake_conf_file(self.fake_conf_file, product_flag,
username_flag, pool_node_flag,
timeout_flag, wait_interval_flag,
alloctype_value)
alloctype_value, multi_url)
self.addCleanup(os.remove, self.fake_conf_file)