From d2abb50840c8ff42f826b6aa08eaebdbc13aaeb2 Mon Sep 17 00:00:00 2001 From: zhaohua Date: Thu, 2 Jul 2015 11:43:20 +0800 Subject: [PATCH] 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 --- manila/share/drivers/huawei/constants.py | 3 + manila/share/drivers/huawei/v3/helper.py | 79 +++++++++++++++---- .../share/drivers/huawei/test_huawei_nas.py | 56 +++++++++++-- 3 files changed, 115 insertions(+), 23 deletions(-) diff --git a/manila/share/drivers/huawei/constants.py b/manila/share/drivers/huawei/constants.py index 075ba02a..3e72b272 100644 --- a/manila/share/drivers/huawei/constants.py +++ b/manila/share/drivers/huawei/constants.py @@ -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 diff --git a/manila/share/drivers/huawei/v3/helper.py b/manila/share/drivers/huawei/v3/helper.py index 721ce8df..f16ac41a 100644 --- a/manila/share/drivers/huawei/v3/helper.py +++ b/manila/share/drivers/huawei/v3/helper.py @@ -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" diff --git a/manila/tests/share/drivers/huawei/test_huawei_nas.py b/manila/tests/share/drivers/huawei/test_huawei_nas.py index 9043fe02..2d25fd7e 100644 --- a/manila/tests/share/drivers/huawei/test_huawei_nas.py +++ b/manila/tests/share/drivers/huawei/test_huawei_nas.py @@ -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)