From 6b93625a3d7e3085bab0246ae4d0fb46b6af7e98 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 29 Jun 2015 15:33:12 +0200 Subject: [PATCH] Port dothill to Python 3 * Replace urllib2 with six.moves.urllib * On Python 3, encode string to UTF-8 to hash it using MD5 * On Python 3, decode base64 from ASCII to get Unicode * Replace "pattern in exc" with "pattern in exc.args" * test_initialize_connection(): fix get_active_iscsi_target_portals() mock. Use the return_value attribute to return a dictionary, instead of returning a single IP address. * tox.ini: add cinder.tests.unit.test_dothill to Python 3.4 Blueprint cinder-python3 Change-Id: Ib20bca813c2352eae447c374ded75c6dafb2e18d --- cinder/tests/unit/test_dothill.py | 10 +++++----- .../volume/drivers/dothill/dothill_client.py | 18 +++++++++++------- .../volume/drivers/dothill/dothill_common.py | 6 ++++-- tox.ini | 1 + 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/cinder/tests/unit/test_dothill.py b/cinder/tests/unit/test_dothill.py index 4438da474..e3a3cd7da 100644 --- a/cinder/tests/unit/test_dothill.py +++ b/cinder/tests/unit/test_dothill.py @@ -15,10 +15,10 @@ # """Unit tests for OpenStack Cinder DotHill driver.""" -import urllib2 from lxml import etree import mock +from six.moves import urllib from cinder import exception from cinder import test @@ -149,7 +149,7 @@ class TestDotHillClient(test.TestCase): self.client = dothill.DotHillClient(self.ip, self.login, self.passwd, self.protocol) - @mock.patch('urllib2.urlopen') + @mock.patch('six.moves.urllib.request.urlopen') def test_login(self, mock_url_open): m = mock.Mock() m.read.side_effect = [resp_login] @@ -175,13 +175,13 @@ class TestDotHillClient(test.TestCase): arg2='val2') self.assertEqual('http://10.0.0.1/api/path/arg2/val2/arg1/arg3', url) - @mock.patch('urllib2.urlopen') + @mock.patch('six.moves.urllib.request.urlopen') def test_request(self, mock_url_open): self.client._session_key = session_key m = mock.Mock() m.read.side_effect = [response_ok, malformed_xml, - urllib2.URLError("error")] + urllib.error.URLError("error")] mock_url_open.return_value = m ret = self.client._request('/path') self.assertTrue(type(ret) == etree._Element) @@ -706,7 +706,7 @@ class TestDotHillISCSI(TestDotHillFC): self.driver.iscsi_ips = ['10.0.0.11'] self.driver.initialize_iscsi_ports() mock_iqns.side_effect = [['id2']] - mock_portals.side_effect = {'10.0.0.11': 'Up', '10.0.0.12': 'Up'} + mock_portals.return_value = {'10.0.0.11': 'Up', '10.0.0.12': 'Up'} self.assertRaises(exception.Invalid, self.driver.initialize_connection, test_volume, diff --git a/cinder/volume/drivers/dothill/dothill_client.py b/cinder/volume/drivers/dothill/dothill_client.py index b5975dc8f..a2054bdaa 100644 --- a/cinder/volume/drivers/dothill/dothill_client.py +++ b/cinder/volume/drivers/dothill/dothill_client.py @@ -17,10 +17,11 @@ from hashlib import md5 import math import time -import urllib2 from lxml import etree from oslo_log import log as logging +import six +from six.moves import urllib from cinder import exception from cinder.i18n import _LE @@ -44,13 +45,16 @@ class DotHillClient(object): def login(self): """Authenticates the service on the device.""" - hash_ = md5("%s_%s" % (self._login, self._password)) + hash_ = "%s_%s" % (self._login, self._password) + if six.PY3: + hash_ = hash_.encode('utf-8') + hash_ = md5(hash_) digest = hash_.hexdigest() url = self._base_url + "/login/" + digest try: - xml = urllib2.urlopen(url).read() - except urllib2.URLError: + xml = urllib.request.urlopen(url).read() + except urllib.error.URLError: raise exception.DotHillConnectionError self._get_auth_token(xml) @@ -91,9 +95,9 @@ class DotHillClient(object): url = self._build_request_url(path, *args, **kargs) headers = {'dataType': 'api', 'sessionKey': self._session_key} - req = urllib2.Request(url, headers=headers) + req = urllib.request.Request(url, headers=headers) try: - xml = urllib2.urlopen(req).read() + xml = urllib.request.urlopen(req).read() tree = etree.XML(xml) except Exception: raise exception.DotHillConnectionError @@ -106,7 +110,7 @@ class DotHillClient(object): def logout(self): url = self._base_url + '/exit' try: - urllib2.urlopen(url) + urllib.request.urlopen(url) return True except Exception: return False diff --git a/cinder/volume/drivers/dothill/dothill_common.py b/cinder/volume/drivers/dothill/dothill_common.py index 271317705..9f23787e6 100644 --- a/cinder/volume/drivers/dothill/dothill_common.py +++ b/cinder/volume/drivers/dothill/dothill_common.py @@ -137,6 +137,8 @@ class DotHillCommon(object): uuid_str = name.replace("-", "") vol_uuid = uuid.UUID('urn:uuid:%s' % uuid_str) vol_encoded = base64.b64encode(vol_uuid.bytes) + if six.PY3: + vol_encoded = vol_encoded.decode('ascii') vol_encoded = vol_encoded.replace('=', '') # + is not a valid character for DotHill @@ -272,7 +274,7 @@ class DotHillCommon(object): self.client.delete_volume(volume_name) except exception.DotHillRequestError as ex: # if the volume wasn't found, ignore the error - if 'The volume was not found on this system.' in ex: + if 'The volume was not found on this system.' in ex.args: return LOG.exception(_LE("Deletion of volume %s failed."), volume['id']) raise exception.Invalid(ex) @@ -411,7 +413,7 @@ class DotHillCommon(object): self.client.delete_snapshot(snap_name) except exception.DotHillRequestError as ex: # if the volume wasn't found, ignore the error - if 'The volume was not found on this system.' in ex: + if 'The volume was not found on this system.' in ex.args: return LOG.exception(_LE("Deleting snapshot %s failed"), snapshot['id']) raise exception.Invalid(ex) diff --git a/tox.ini b/tox.ini index 0007c5604..7c36705e4 100644 --- a/tox.ini +++ b/tox.ini @@ -44,6 +44,7 @@ commands = cinder.tests.unit.test_dellfc \ cinder.tests.unit.test_dellsc \ cinder.tests.unit.test_dellscapi \ + cinder.tests.unit.test_dothill \ cinder.tests.unit.test_drbdmanagedrv \ cinder.tests.unit.test_emc_xtremio \ cinder.tests.unit.test_eqlx \ -- 2.45.2