From: Victor Rodionov Date: Tue, 8 Oct 2013 20:11:09 +0000 (+0400) Subject: Nexenta volume drivers: refactor NexentaJSONProxy X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=87f47e1595eb8307a7212ccd5224651e4e61fcb8;p=openstack-build%2Fcinder-build.git Nexenta volume drivers: refactor NexentaJSONProxy Change constructor arguments of NexentaJSONProxy. Change-Id: I43d69485e6a98704f4cb02a4b55acccf9c05b683 --- diff --git a/cinder/tests/test_nexenta.py b/cinder/tests/test_nexenta.py index cdafafa5d..3742b5acd 100644 --- a/cinder/tests/test_nexenta.py +++ b/cinder/tests/test_nexenta.py @@ -23,7 +23,6 @@ import base64 import urllib2 import mox as mox_lib -from mox import stubout from cinder import test from cinder import units @@ -270,19 +269,22 @@ class TestNexentaDriver(test.TestCase): class TestNexentaJSONRPC(test.TestCase): - URL = 'http://example.com/' - URL_S = 'https://example.com/' + HOST = 'example.com' + URL = 'http://%s/' % HOST + URL_S = 'https://%s/' % HOST USER = 'user' PASSWORD = 'password' - HEADERS = {'Authorization': 'Basic %s' % ( - base64.b64encode(':'.join((USER, PASSWORD))),), - 'Content-Type': 'application/json'} + HEADERS = { + 'Authorization': + 'Basic %s' % base64.b64encode('%s:%s' % (USER, PASSWORD)), + 'Content-Type': 'application/json' + } REQUEST = 'the request' def setUp(self): super(TestNexentaJSONRPC, self).setUp() self.proxy = jsonrpc.NexentaJSONProxy( - self.URL, self.USER, self.PASSWORD, auto=True) + 'http', self.HOST, 2000, '/', self.USER, self.PASSWORD, auto=True) self.mox.StubOutWithMock(urllib2, 'Request', True) self.mox.StubOutWithMock(urllib2, 'urlopen') self.resp_mock = self.mox.CreateMockAnything() @@ -292,7 +294,7 @@ class TestNexentaJSONRPC(test.TestCase): def test_call(self): urllib2.Request( - self.URL, + 'http://%s:2000/' % self.HOST, '{"object": null, "params": ["arg1", "arg2"], "method": null}', self.HEADERS).AndReturn(self.REQUEST) self.resp_info_mock.status = '' @@ -304,7 +306,7 @@ class TestNexentaJSONRPC(test.TestCase): def test_call_deep(self): urllib2.Request( - self.URL, + 'http://%s:2000/' % self.HOST, '{"object": "obj1.subobj", "params": ["arg1", "arg2"],' ' "method": "meth"}', self.HEADERS).AndReturn(self.REQUEST) @@ -317,11 +319,11 @@ class TestNexentaJSONRPC(test.TestCase): def test_call_auto(self): urllib2.Request( - self.URL, + 'http://%s:2000/' % self.HOST, '{"object": null, "params": ["arg1", "arg2"], "method": null}', self.HEADERS).AndReturn(self.REQUEST) urllib2.Request( - self.URL_S, + 'https://%s:2000/' % self.HOST, '{"object": null, "params": ["arg1", "arg2"], "method": null}', self.HEADERS).AndReturn(self.REQUEST) self.resp_info_mock.status = 'EOF in headers' @@ -334,7 +336,7 @@ class TestNexentaJSONRPC(test.TestCase): def test_call_error(self): urllib2.Request( - self.URL, + 'http://%s:2000/' % self.HOST, '{"object": null, "params": ["arg1", "arg2"], "method": null}', self.HEADERS).AndReturn(self.REQUEST) self.resp_info_mock.status = '' @@ -346,7 +348,7 @@ class TestNexentaJSONRPC(test.TestCase): def test_call_fail(self): urllib2.Request( - self.URL, + 'http://%s:2000/' % self.HOST, '{"object": null, "params": ["arg1", "arg2"], "method": null}', self.HEADERS).AndReturn(self.REQUEST) self.resp_info_mock.status = 'EOF in headers' diff --git a/cinder/volume/drivers/nexenta/jsonrpc.py b/cinder/volume/drivers/nexenta/jsonrpc.py index ddeb5bdd8..ed2d20a8d 100644 --- a/cinder/volume/drivers/nexenta/jsonrpc.py +++ b/cinder/volume/drivers/nexenta/jsonrpc.py @@ -20,6 +20,7 @@ .. automodule:: nexenta.jsonrpc .. moduleauthor:: Yuriy Taraday +.. moduleauthor:: Victor Rodionov """ import urllib2 @@ -36,8 +37,13 @@ class NexentaJSONException(nexenta.NexentaException): class NexentaJSONProxy(object): - def __init__(self, url, user, password, auto=False, obj=None, method=None): - self.url = url + + def __init__(self, scheme, host, port, path, user, password, auto=False, + obj=None, method=None): + self.scheme = scheme.lower() + self.host = host + self.port = port + self.path = path self.user = user self.password = password self.auto = auto @@ -51,34 +57,46 @@ class NexentaJSONProxy(object): obj, method = self.obj, name else: obj, method = '%s.%s' % (self.obj, self.method), name - return NexentaJSONProxy(self.url, self.user, self.password, self.auto, - obj, method) + return NexentaJSONProxy(self.scheme, self.host, self.port, self.path, + self.user, self.password, self.auto, obj, + method) + + @property + def url(self): + return '%s://%s:%s%s' % (self.scheme, self.host, self.port, self.path) + + def __hash__(self): + return self.url.__hash__() + + def __repr__(self): + return 'NMS proxy: %s' % self.url def __call__(self, *args): - data = jsonutils.dumps({'object': self.obj, - 'method': self.method, - 'params': args}) + data = jsonutils.dumps({ + 'object': self.obj, + 'method': self.method, + 'params': args + }) auth = ('%s:%s' % (self.user, self.password)).encode('base64')[:-1] - headers = {'Content-Type': 'application/json', - 'Authorization': 'Basic %s' % (auth,)} + headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Basic %s' % auth + } LOG.debug(_('Sending JSON data: %s'), data) request = urllib2.Request(self.url, data, headers) response_obj = urllib2.urlopen(request) if response_obj.info().status == 'EOF in headers': - if self.auto and self.url.startswith('http://'): - LOG.info(_('Auto switching to HTTPS connection to %s'), - self.url) - self.url = 'https' + self.url[4:] - request = urllib2.Request(self.url, data, headers) - response_obj = urllib2.urlopen(request) - else: + if not self.auto or self.scheme != 'http': LOG.error(_('No headers in server response')) raise NexentaJSONException(_('Bad response from server')) + LOG.info(_('Auto switching to HTTPS connection to %s'), self.url) + self.scheme = 'https' + request = urllib2.Request(self.url, data, headers) + response_obj = urllib2.urlopen(request) response_data = response_obj.read() LOG.debug(_('Got response: %s'), response_data) response = jsonutils.loads(response_data) if response.get('error') is not None: raise NexentaJSONException(response['error'].get('message', '')) - else: - return response.get('result') + return response.get('result') diff --git a/cinder/volume/drivers/nexenta/nfs.py b/cinder/volume/drivers/nexenta/nfs.py index e3b298159..ccf6c1490 100644 --- a/cinder/volume/drivers/nexenta/nfs.py +++ b/cinder/volume/drivers/nexenta/nfs.py @@ -349,28 +349,12 @@ class NexentaNfsDriver(nfs.NfsDriver): # pylint: disable=R0921 allocated = utils.str2size(folder_props['used']) return free + allocated, free, allocated - def _get_nms_for_url(self, nms_url): - pr = urlparse.urlparse(nms_url) - scheme = pr.scheme - auto = scheme == 'auto' - if auto: - scheme = 'http' - user = 'admin' - password = 'nexenta' - if '@' not in pr.netloc: - host_and_port = pr.netloc - else: - user_and_password, host_and_port = pr.netloc.split('@', 1) - if ':' in user_and_password: - user, password = user_and_password.split(':') - else: - user = user_and_password - if ':' in host_and_port: - host, port = host_and_port.split(':', 1) - else: - host, port = host_and_port, '2000' - url = '%s://%s:%s/rest/nms/' % (scheme, host, port) - return jsonrpc.NexentaJSONProxy(url, user, password, auto=auto) + def _get_nms_for_url(self, url): + """Returns initialized nms object for url.""" + auto, scheme, user, password, host, port, path =\ + utils.parse_nms_url(url) + return jsonrpc.NexentaJSONProxy(scheme, host, port, path, user, + password, auto=auto) def _get_snapshot_volume(self, snapshot): ctxt = context.get_admin_context() diff --git a/cinder/volume/drivers/nexenta/utils.py b/cinder/volume/drivers/nexenta/utils.py index 5a0fc7229..03b2cd385 100644 --- a/cinder/volume/drivers/nexenta/utils.py +++ b/cinder/volume/drivers/nexenta/utils.py @@ -14,8 +14,17 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +""" +:mod:`nexenta.utils` -- Nexenta-specific utils functions. +========================================================= + +.. automodule:: nexenta.utils +.. moduleauthor:: Victor Rodionov +.. moduleauthor:: Mikhail Khodos +""" import re +import urlparse def str2size(s, scale=1024): @@ -40,7 +49,48 @@ def str2size(s, scale=1024): value = float(groups[0]) suffix = len(groups) > 1 and groups[1].upper() or 'B' - types = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + types = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') for i, t in enumerate(types): if suffix == t: return int(value * pow(scale, i)) + + +def parse_nms_url(url): + """Parse NMS url into normalized parts like scheme, user, host and others. + + Example NMS URL: + auto://admin:nexenta@192.168.1.1:2000/ + + NMS URL parts: + auto True if url starts with auto://, protocol will be + automatically switched to https if http not + supported; + scheme (auto) connection protocol (http or https); + user (admin) NMS user; + password (nexenta) NMS password; + host (192.168.1.1) NMS host; + port (2000) NMS port. + + :param url: url string + :return: tuple (auto, scheme, user, password, host, port, path) + """ + pr = urlparse.urlparse(url) + scheme = pr.scheme + auto = scheme == 'auto' + if auto: + scheme = 'http' + user = 'admin' + password = 'nexenta' + if '@' not in pr.netloc: + host_and_port = pr.netloc + else: + user_and_password, host_and_port = pr.netloc.split('@', 1) + if ':' in user_and_password: + user, password = user_and_password.split(':') + else: + user = user_and_password + if ':' in host_and_port: + host, port = host_and_port.split(':', 1) + else: + host, port = host_and_port, '2000' + return auto, scheme, user, password, host, port, '/rest/nms/' diff --git a/cinder/volume/drivers/nexenta/volume.py b/cinder/volume/drivers/nexenta/volume.py index edcb63272..0792d3f61 100644 --- a/cinder/volume/drivers/nexenta/volume.py +++ b/cinder/volume/drivers/nexenta/volume.py @@ -69,11 +69,10 @@ class NexentaDriver(driver.ISCSIDriver): # pylint: disable=R0921 auto = protocol == 'auto' if auto: protocol = 'http' - url = '%s://%s:%s/rest/nms/' % (protocol, - self.configuration.nexenta_host, - self.configuration.nexenta_rest_port) self.nms = jsonrpc.NexentaJSONProxy( - url, self.configuration.nexenta_user, + protocol, self.configuration.nexenta_host, + self.configuration.nexenta_rest_port, '/rest/nms', + self.configuration.nexenta_user, self.configuration.nexenta_password, auto=auto) def check_for_setup_error(self):