More compatibility with Python 3

- use the six version of configparser and urllib, and depends on six;
- remove relative imports;
- adapt few tests to the changes.

The changes above should be noop from the point of view of
functionalities, at least on python 2.

And also:
- replace the py34 tox virtualenv with py35;
- add a non-voting py35 job (locally for now, it will be enabled
  to project-config also for gating when stable).

Story: 2002574
Task: 22142
Change-Id: I0a35abaae6f5b7095ebae765fbe2163046e0a4da
This commit is contained in:
Luigi Toscano
2018-06-26 23:20:52 +02:00
parent 16f0a1c66e
commit cdbc98572d
19 changed files with 81 additions and 65 deletions

View File

@@ -1,6 +1,8 @@
- project: - project:
check: check:
jobs: jobs:
- openstack-tox-py35:
voting: false
- python-tempestconf-tox-cover - python-tempestconf-tox-cover
- python-tempestconf-tempest-devstack-admin - python-tempestconf-tempest-devstack-admin
- python-tempestconf-tempest-devstack-demo - python-tempestconf-tempest-devstack-demo

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from constants import LOG from config_tempest.constants import LOG
class Flavors(object): class Flavors(object):

View File

@@ -37,22 +37,23 @@ obtained by querying the cloud.
""" """
import argparse import argparse
import ConfigParser
import logging import logging
import os import os
import sys import sys
import accounts
from clients import ClientManager
import constants as C
from constants import LOG
from credentials import Credentials
from flavors import Flavors
import os_client_config import os_client_config
from oslo_config import cfg from oslo_config import cfg
from services.services import Services from six.moves import configparser
import tempest_conf
from users import Users from config_tempest import accounts
from config_tempest.clients import ClientManager
from config_tempest import constants as C
from config_tempest.constants import LOG
from config_tempest.credentials import Credentials
from config_tempest.flavors import Flavors
from config_tempest.services.services import Services
from config_tempest.tempest_conf import TempestConf
from config_tempest.users import Users
def set_logging(debug, verbose): def set_logging(debug, verbose):
@@ -111,7 +112,7 @@ def read_deployer_input(deployer_input_file, conf):
""" """
LOG.info("Adding options from deployer-input file '%s'", LOG.info("Adding options from deployer-input file '%s'",
deployer_input_file) deployer_input_file)
deployer_input = ConfigParser.SafeConfigParser() deployer_input = configparser.SafeConfigParser()
deployer_input.read(deployer_input_file) deployer_input.read(deployer_input_file)
for section in deployer_input.sections(): for section in deployer_input.sections():
# There are no deployer input options in DEFAULT # There are no deployer input options in DEFAULT
@@ -368,7 +369,7 @@ def config_tempest(**kwargs):
set_logging(kwargs.get('debug', False), kwargs.get('verbose', False)) set_logging(kwargs.get('debug', False), kwargs.get('verbose', False))
write_credentials = kwargs.get('test_accounts') is None write_credentials = kwargs.get('test_accounts') is None
conf = tempest_conf.TempestConf(write_credentials=write_credentials) conf = TempestConf(write_credentials=write_credentials)
set_options(conf, kwargs.get('deployer_input'), set_options(conf, kwargs.get('deployer_input'),
kwargs.get('non_admin', False), kwargs.get('non_admin', False),
kwargs.get('overrides', []), kwargs.get('test_accounts'), kwargs.get('overrides', []), kwargs.get('test_accounts'),

View File

@@ -16,7 +16,8 @@
import json import json
import re import re
import urllib3 import urllib3
import urlparse
from six.moves import urllib
from config_tempest.constants import LOG from config_tempest.constants import LOG
MULTIPLE_SLASH = re.compile(r'/+') MULTIPLE_SLASH = re.compile(r'/+')
@@ -39,13 +40,13 @@ class Service(object):
self.versions = [] self.versions = []
def do_get(self, url, top_level=False, top_level_path=""): def do_get(self, url, top_level=False, top_level_path=""):
parts = list(urlparse.urlparse(url)) parts = list(urllib.parse.urlparse(url))
# 2 is the path offset # 2 is the path offset
if top_level: if top_level:
parts[2] = '/' + top_level_path parts[2] = '/' + top_level_path
parts[2] = MULTIPLE_SLASH.sub('/', parts[2]) parts[2] = MULTIPLE_SLASH.sub('/', parts[2])
url = urlparse.urlunparse(parts) url = urllib.parse.urlunparse(parts)
try: try:
if self.disable_ssl_validation: if self.disable_ssl_validation:

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import config_tempest.constants as C from config_tempest import constants as C
from tempest.lib import exceptions from tempest.lib import exceptions

View File

@@ -13,11 +13,13 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from base import VersionedService
import config_tempest.constants as C
import json import json
from tempest.lib import exceptions from tempest.lib import exceptions
from config_tempest import constants as C
from config_tempest.services.base import VersionedService
class ComputeService(VersionedService): class ComputeService(VersionedService):
def set_extensions(self): def set_extensions(self):

View File

@@ -13,20 +13,20 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import urllib2 from six.moves import urllib
def configure_horizon(conf): def configure_horizon(conf):
"""Derive the horizon URIs from the identity's URI.""" """Derive the horizon URIs from the identity's URI."""
uri = conf.get('identity', 'uri') uri = conf.get('identity', 'uri')
u = urllib2.urlparse.urlparse(uri) u = urllib.parse.urlparse(uri)
base = '%s://%s%s' % (u.scheme, u.netloc.replace( base = '%s://%s%s' % (u.scheme, u.netloc.replace(
':' + str(u.port), ''), '/dashboard') ':' + str(u.port), ''), '/dashboard')
assert base.startswith('http:') or base.startswith('https:') assert base.startswith('http:') or base.startswith('https:')
has_horizon = True has_horizon = True
try: try:
urllib2.urlopen(base) urllib.request.urlopen(base)
except urllib2.URLError: except urllib.error.URLError:
has_horizon = False has_horizon = False
conf.set('service_available', 'horizon', str(has_horizon)) conf.set('service_available', 'horizon', str(has_horizon))
conf.set('dashboard', 'dashboard_url', base + '/') conf.set('dashboard', 'dashboard_url', base + '/')

View File

@@ -15,10 +15,11 @@
import json import json
import requests import requests
import urlparse
from base import VersionedService from six.moves import urllib
from config_tempest.constants import LOG from config_tempest.constants import LOG
from config_tempest.services.base import VersionedService
class IdentityService(VersionedService): class IdentityService(VersionedService):
@@ -30,7 +31,7 @@ class IdentityService(VersionedService):
version = '' version = ''
if 'v2' in self.service_url: if 'v2' in self.service_url:
version = '/v2.0' version = '/v2.0'
url_parse = urlparse.urlparse(self.service_url) url_parse = urllib.parse.urlparse(self.service_url)
self.service_url = '{}://{}{}'.format(url_parse.scheme, self.service_url = '{}://{}{}'.format(url_parse.scheme,
url_parse.netloc, version) url_parse.netloc, version)

View File

@@ -15,12 +15,13 @@
import os import os
import shutil import shutil
import urllib2
from base import VersionedService from six.moves import urllib
from config_tempest.constants import LOG
from tempest.lib import exceptions from tempest.lib import exceptions
from config_tempest.constants import LOG
from config_tempest.services.base import VersionedService
class ImageService(VersionedService): class ImageService(VersionedService):
@@ -173,7 +174,7 @@ class ImageService(VersionedService):
LOG.info("Image '%s' already fetched to '%s'.", url, destination) LOG.info("Image '%s' already fetched to '%s'.", url, destination)
return return
LOG.info("Downloading '%s' and saving as '%s'", url, destination) LOG.info("Downloading '%s' and saving as '%s'", url, destination)
f = urllib2.urlopen(url) f = urllib.request.urlopen(url)
data = f.read() data = f.read()
with open(destination, "wb") as dest: with open(destination, "wb") as dest:
dest.write(data) dest.write(data)

View File

@@ -15,8 +15,8 @@
import json import json
from base import VersionedService
from config_tempest.constants import LOG from config_tempest.constants import LOG
from config_tempest.services.base import VersionedService
class NetworkService(VersionedService): class NetworkService(VersionedService):

View File

@@ -13,13 +13,14 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ConfigParser
import json import json
from base import Service from six.moves import configparser
from config_tempest.constants import LOG
from tempest.lib import exceptions from tempest.lib import exceptions
from config_tempest.constants import LOG
from config_tempest.services.base import Service
class ObjectStorageService(Service): class ObjectStorageService(Service):
def set_extensions(self): def set_extensions(self):
@@ -71,7 +72,7 @@ class ObjectStorageService(Service):
'object-storage-feature-enabled', 'object-storage-feature-enabled',
'discoverability')): 'discoverability')):
return False return False
except ConfigParser.NoSectionError: except configparser.NoSectionError:
# Turning http://.../v1/foobar into http://.../ # Turning http://.../v1/foobar into http://.../
self.client.accounts.skip_path() self.client.accounts.skip_path()
resp, _ = self.client.accounts.get("healthcheck", {}) resp, _ = self.client.accounts.get("healthcheck", {})

View File

@@ -13,20 +13,22 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import urlparse
from base import Service from six.moves import urllib
import boto
import ceilometer from config_tempest import constants as C
from compute import ComputeService from config_tempest.services.base import Service
import config_tempest.constants as C from config_tempest.services import boto
import horizon from config_tempest.services import ceilometer
from identity import IdentityService from config_tempest.services.compute import ComputeService
from image import ImageService from config_tempest.services import horizon
from network import NetworkService from config_tempest.services.identity import IdentityService
from object_storage import ObjectStorageService from config_tempest.services.image import ImageService
from octavia import LoadBalancerService from config_tempest.services.network import NetworkService
import volume from config_tempest.services.object_storage import ObjectStorageService
from config_tempest.services.octavia import LoadBalancerService
from config_tempest.services import volume
service_dict = {'compute': ComputeService, service_dict = {'compute': ComputeService,
'image': ImageService, 'image': ImageService,
@@ -148,7 +150,7 @@ class Services(object):
# self._clients.auth_provider.auth_url stores identity.uri(_v3) value # self._clients.auth_provider.auth_url stores identity.uri(_v3) value
# from TempestConf # from TempestConf
port = urlparse.urlparse(self._clients.auth_provider.auth_url).port port = urllib.parse.urlparse(self._clients.auth_provider.auth_url).port
if port is None: if port is None:
port = "" port = ""
else: else:

View File

@@ -15,10 +15,11 @@
import json import json
from base import VersionedService
import config_tempest.constants as C
from tempest.lib import exceptions from tempest.lib import exceptions
from config_tempest import constants as C
from config_tempest.services.base import VersionedService
class VolumeService(VersionedService): class VolumeService(VersionedService):
def set_extensions(self): def set_extensions(self):

View File

@@ -13,16 +13,16 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ConfigParser
import os import os
import sys import sys
import constants as C from config_tempest import constants as C
from oslo_config import cfg from oslo_config import cfg
from six.moves import configparser
import tempest.config import tempest.config
class TempestConf(ConfigParser.SafeConfigParser): class TempestConf(configparser.SafeConfigParser):
# causes the config parser to preserve case of the options # causes the config parser to preserve case of the options
optionxform = str optionxform = str
@@ -34,7 +34,7 @@ class TempestConf(ConfigParser.SafeConfigParser):
def __init__(self, write_credentials=True, **kwargs): def __init__(self, write_credentials=True, **kwargs):
self.write_credentials = write_credentials self.write_credentials = write_credentials
ConfigParser.SafeConfigParser.__init__(self, **kwargs) configparser.SafeConfigParser.__init__(self, **kwargs)
def get_bool_value(self, value): def get_bool_value(self, value):
"""Returns boolean value of the string value given. """Returns boolean value of the string value given.
@@ -101,7 +101,7 @@ class TempestConf(ConfigParser.SafeConfigParser):
if priority: if priority:
self.priority_sectionkeys.add((section, key)) self.priority_sectionkeys.add((section, key))
C.LOG.debug("Setting [%s] %s = %s", section, key, value) C.LOG.debug("Setting [%s] %s = %s", section, key, value)
ConfigParser.SafeConfigParser.set(self, section, key, value) configparser.SafeConfigParser.set(self, section, key, value)
return True return True
def write(self, out_path): def write(self, out_path):
@@ -111,7 +111,7 @@ class TempestConf(ConfigParser.SafeConfigParser):
"writing credentials is disabled.") "writing credentials is disabled.")
self.remove_values(C.ALL_CREDENTIALS_KEYS) self.remove_values(C.ALL_CREDENTIALS_KEYS)
with open(out_path, 'w') as f: with open(out_path, 'w') as f:
ConfigParser.SafeConfigParser.write(self, f) configparser.SafeConfigParser.write(self, f)
def remove_values(self, to_remove): def remove_values(self, to_remove):
"""Remove values from configuration file specified in arguments. """Remove values from configuration file specified in arguments.
@@ -138,9 +138,9 @@ class TempestConf(ConfigParser.SafeConfigParser):
# and preserve the original order of items # and preserve the original order of items
conf_values = [v for v in conf_values if v not in remove] conf_values = [v for v in conf_values if v not in remove]
self.set(section, key, ",".join(conf_values)) self.set(section, key, ",".join(conf_values))
except ConfigParser.NoOptionError: except configparser.NoOptionError:
# only inform a user, option specified by him doesn't exist # only inform a user, option specified by him doesn't exist
C.LOG.error(sys.exc_info()[1]) C.LOG.error(sys.exc_info()[1])
except ConfigParser.NoSectionError: except configparser.NoSectionError:
# only inform a user, section specified by him doesn't exist # only inform a user, section specified by him doesn't exist
C.LOG.error(sys.exc_info()[1]) C.LOG.error(sys.exc_info()[1])

View File

@@ -13,7 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ConfigParser
from six.moves import configparser
from config_tempest.services import ceilometer from config_tempest.services import ceilometer
from config_tempest.tempest_conf import TempestConf from config_tempest.tempest_conf import TempestConf
@@ -29,7 +30,7 @@ class TestCeilometerService(BaseServiceTest):
client_service_mock = self.FakeServiceClient(services={}) client_service_mock = self.FakeServiceClient(services={})
ceilometer.check_ceilometer_service(self.conf, client_service_mock) ceilometer.check_ceilometer_service(self.conf, client_service_mock)
self._assert_conf_get_not_raises(ConfigParser.NoSectionError, self._assert_conf_get_not_raises(configparser.NoSectionError,
"service_available", "service_available",
"ceilometer") "ceilometer")

View File

@@ -28,7 +28,8 @@ class TestConfigTempest(BaseConfigTempestTest):
def test_configure_horizon_ipv4(self): def test_configure_horizon_ipv4(self):
mock_function = mock.Mock(return_value=True) mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function)) self.useFixture(MonkeyPatch('six.moves.urllib.request.urlopen',
mock_function))
horizon.configure_horizon(self.conf) horizon.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True") self.assertEqual(self.conf.get('service_available', 'horizon'), "True")
self.assertEqual(self.conf.get('dashboard', 'dashboard_url'), self.assertEqual(self.conf.get('dashboard', 'dashboard_url'),
@@ -38,7 +39,8 @@ class TestConfigTempest(BaseConfigTempestTest):
def test_configure_horizon_ipv6(self): def test_configure_horizon_ipv6(self):
mock_function = mock.Mock(return_value=True) mock_function = mock.Mock(return_value=True)
self.useFixture(MonkeyPatch('urllib2.urlopen', mock_function)) self.useFixture(MonkeyPatch('six.moves.urllib.request.urlopen',
mock_function))
self.conf.set('identity', 'uri', 'http://[::1]:5000/v3', priority=True) self.conf.set('identity', 'uri', 'http://[::1]:5000/v3', priority=True)
horizon.configure_horizon(self.conf) horizon.configure_horizon(self.conf)
self.assertEqual(self.conf.get('service_available', 'horizon'), "True") self.assertEqual(self.conf.get('service_available', 'horizon'), "True")

View File

@@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from constants import LOG from config_tempest.constants import LOG
from tempest.lib import exceptions from tempest.lib import exceptions

View File

@@ -3,6 +3,7 @@
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
pbr>=1.8 # Apache-2.0 pbr>=1.8 # Apache-2.0
six>=1.10.0 # MIT
tempest>=14.0.0 # Apache-2.0 tempest>=14.0.0 # Apache-2.0
requests>=2.10.0,!=2.12.2 # Apache-2.0 requests>=2.10.0,!=2.12.2 # Apache-2.0
os-client-config>=1.26.0 # Apache-2.0 os-client-config>=1.26.0 # Apache-2.0

View File

@@ -1,6 +1,6 @@
[tox] [tox]
minversion = 2.0 minversion = 2.0
envlist = py34,py27,pypy,pep8 envlist = py35,py27,pypy,pep8
skipsdist = True skipsdist = True
[testenv] [testenv]