diff --git a/designate_tempest_plugin/clients.py b/designate_tempest_plugin/clients.py index c6eed6f5..012f8a36 100644 --- a/designate_tempest_plugin/clients.py +++ b/designate_tempest_plugin/clients.py @@ -15,16 +15,16 @@ from tempest import clients from tempest import config from tempest.lib import auth -from designate_tempest_plugin.services.dns.v2.json.blacklists_client import \ - BlacklistsClient -from designate_tempest_plugin.services.dns.v2.json.pool_client import \ - PoolClient -from designate_tempest_plugin.services.dns.v2.json.recordset_client import \ - RecordsetClient -from designate_tempest_plugin.services.dns.v2.json.tld_client import \ - TldClient -from designate_tempest_plugin.services.dns.v2.json.zones_client import \ - ZonesClient +from designate_tempest_plugin.services.dns.v2.json.blacklists_client import ( + BlacklistsClient) +from designate_tempest_plugin.services.dns.v2.json.pool_client import ( + PoolClient) +from designate_tempest_plugin.services.dns.v2.json.recordset_client import ( + RecordsetClient) +from designate_tempest_plugin.services.dns.v2.json.tld_client import ( + TldClient) +from designate_tempest_plugin.services.dns.v2.json.zones_client import ( + ZonesClient) CONF = config.CONF diff --git a/designate_tempest_plugin/data_utils.py b/designate_tempest_plugin/data_utils.py index d148685e..d56d0de6 100644 --- a/designate_tempest_plugin/data_utils.py +++ b/designate_tempest_plugin/data_utils.py @@ -95,8 +95,8 @@ def rand_quotas(zones=None, zone_records=None, zone_recordsets=None, } if CONF.dns_feature_enabled.bug_1573141_fixed: - quotas_dict['api_export_size'] = \ - api_export_size or data_utils.rand_int_id(100, 999999) + quotas_dict['api_export_size'] = ( + api_export_size or data_utils.rand_int_id(100, 999999)) else: LOG.warning("Leaving `api_export_size` out of quota data due to: " "https://bugs.launchpad.net/designate/+bug/1573141") @@ -190,8 +190,7 @@ def rand_sshfp_recordset(zone_name, algorithm_number=None, **kwargs): algorithm_number = algorithm_number or 2 fingerprint_type = fingerprint_type or 1 - fingerprint = fingerprint or \ - "123456789abcdef67890123456789abcdef67890" + fingerprint = fingerprint or "123456789abcdef67890123456789abcdef67890" data = "%s %s %s" % (algorithm_number, fingerprint_type, fingerprint) return rand_recordset_data('SSHFP', zone_name, records=[data], **kwargs) diff --git a/designate_tempest_plugin/hacking/__init__.py b/designate_tempest_plugin/hacking/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/designate_tempest_plugin/hacking/checks.py b/designate_tempest_plugin/hacking/checks.py new file mode 100644 index 00000000..53de95ce --- /dev/null +++ b/designate_tempest_plugin/hacking/checks.py @@ -0,0 +1,187 @@ +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2012, Cloudscaling +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import re + +from hacking import core +import pycodestyle + +# D701: Default parameter value is a mutable type +# D702: Log messages require translation +# D703: Found use of _() without explicit import of _! +# D704: Found import of %s. This oslo library has been graduated! +# D705: timeutils.utcnow() must be used instead of datetime.%s() +# D706: Don't translate debug level logs +# D707: basestring is not Python3-compatible, use str instead. +# D708: Do not use xrange. Use range for large loops. +# D709: LOG.audit is deprecated, please use LOG.info! +# D710: LOG.warn() is not allowed. Use LOG.warning() +# D711: Don't use backslashes for line continuation. + +UNDERSCORE_IMPORT_FILES = [] + + +mutable_default_argument_check = re.compile( + r"^\s*def .+\((.+=\{\}|.+=\[\])") +string_translation = re.compile(r"[^_]*_\(\s*('|\")") +translated_log = re.compile( + r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)" + r"\(\s*_\(\s*('|\")") +underscore_import_check = re.compile(r"(.)*import _(.)*") +# We need this for cases where they have created their own _ function. +custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*") +graduated_oslo_libraries_import_re = re.compile( + r"^\s*(?:import|from) designate\.openstack\.common\.?.*?" + r"(gettextutils|rpc)" + r".*?") +no_line_continuation_backslash_re = re.compile(r'.*(\\)\n') + + +@core.flake8ext +def mutable_default_arguments(physical_line, logical_line, filename): + if pycodestyle.noqa(physical_line): + return + + if mutable_default_argument_check.match(logical_line): + yield (0, "D701: Default parameter value is a mutable type") + + +@core.flake8ext +def no_translate_debug_logs(logical_line, filename): + """Check for 'LOG.debug(_(' + As per our translation policy, + https://wiki.openstack.org/wiki/LoggingStandards#Log_Translation + we shouldn't translate debug level logs. + * This check assumes that 'LOG' is a logger. + * Use filename so we can start enforcing this in specific folders instead + of needing to do so all at once. + N319 + """ + if logical_line.startswith("LOG.debug(_("): + yield(0, "D706: Don't translate debug level logs") + + +@core.flake8ext +def check_explicit_underscore_import(logical_line, filename): + """Check for explicit import of the _ function + + We need to ensure that any files that are using the _() function + to translate logs are explicitly importing the _ function. We + can't trust unit test to catch whether the import has been + added so we need to check for it here. + """ + # Build a list of the files that have _ imported. No further + # checking needed once it is found. + if filename in UNDERSCORE_IMPORT_FILES: + pass + elif (underscore_import_check.match(logical_line) or + custom_underscore_check.match(logical_line)): + UNDERSCORE_IMPORT_FILES.append(filename) + elif (translated_log.match(logical_line) or + string_translation.match(logical_line)): + yield(0, "D703: Found use of _() without explicit import of _!") + + +@core.flake8ext +def no_import_graduated_oslo_libraries(logical_line, filename): + """Check that we don't continue to use o.c. oslo libraries after graduation + + After a library graduates from oslo-incubator, as we make the switch, we + should ensure we don't continue to use the oslo-incubator versions. + + In many cases, it's not possible to immediately remove the code from the + openstack/common folder due to dependency issues. + """ + # We can't modify oslo-incubator code, so ignore it here. + if "designate/openstack/common" in filename: + return + + matches = graduated_oslo_libraries_import_re.match(logical_line) + if matches: + yield(0, "D704: Found import of %s. This oslo library has been " + "graduated!" % matches.group(1)) + + +@core.flake8ext +def use_timeutils_utcnow(logical_line, filename): + # tools are OK to use the standard datetime module + if "/tools/" in filename: + return + + msg = "D705: timeutils.utcnow() must be used instead of datetime.%s()" + + datetime_funcs = ['now', 'utcnow'] + for f in datetime_funcs: + pos = logical_line.find('datetime.%s' % f) + if pos != -1: + yield (pos, msg % f) + + +@core.flake8ext +def check_no_basestring(logical_line): + if re.search(r"\bbasestring\b", logical_line): + msg = ("D707: basestring is not Python3-compatible, use " + "str instead.") + yield(0, msg) + + +@core.flake8ext +def check_python3_xrange(logical_line): + if re.search(r"\bxrange\s*\(", logical_line): + yield(0, "D708: Do not use xrange. Use range for " + "large loops.") + + +@core.flake8ext +def check_no_log_audit(logical_line): + """Ensure that we are not using LOG.audit messages + Plans are in place going forward as discussed in the following + spec (https://review.opendev.org/#/c/132552/) to take out + LOG.audit messages. Given that audit was a concept invented + for OpenStack we can enforce not using it. + """ + if "LOG.audit(" in logical_line: + yield(0, "D709: LOG.audit is deprecated, please use LOG.info!") + + +@core.flake8ext +def check_no_log_warn(logical_line): + """Disallow 'LOG.warn(' + + D710 + """ + if logical_line.startswith('LOG.warn('): + yield(0, "D710:Use LOG.warning() rather than LOG.warn()") + + +@core.flake8ext +def check_line_continuation_no_backslash(logical_line, tokens): + """D711 - Don't use backslashes for line continuation. + + :param logical_line: The logical line to check. Not actually used. + :param tokens: List of tokens to check. + :returns: None if the tokens don't contain any issues, otherwise a tuple + is yielded that contains the offending index in the logical + line and a message describe the check validation failure. + """ + backslash = None + for token_type, text, start, end, orig_line in tokens: + m = no_line_continuation_backslash_re.match(orig_line) + if m: + backslash = (start[0], m.start(1)) + break + + if backslash is not None: + msg = 'D711 Backslash line continuations not allowed' + yield backslash, msg diff --git a/designate_tempest_plugin/services/dns/admin/__init__.py b/designate_tempest_plugin/services/dns/admin/__init__.py index 2b43e848..a3b6ac0a 100644 --- a/designate_tempest_plugin/services/dns/admin/__init__.py +++ b/designate_tempest_plugin/services/dns/admin/__init__.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations under # the License. -from designate_tempest_plugin.services.dns.admin.json.quotas_client import \ - QuotasClient +from designate_tempest_plugin.services.dns.admin.json.quotas_client import ( + QuotasClient) __all__ = ['QuotasClient'] diff --git a/designate_tempest_plugin/services/dns/v2/__init__.py b/designate_tempest_plugin/services/dns/v2/__init__.py index 44cc403f..ed86ffb8 100644 --- a/designate_tempest_plugin/services/dns/v2/__init__.py +++ b/designate_tempest_plugin/services/dns/v2/__init__.py @@ -12,35 +12,21 @@ # License for the specific language governing permissions and limitations under # the License. -from designate_tempest_plugin.services.dns.v2.json.blacklists_client import \ - BlacklistsClient -from designate_tempest_plugin.services.dns.v2.json.designate_limit_client \ - import DesignateLimitClient -from designate_tempest_plugin.services.dns.v2.json.pool_client import \ - PoolClient -from designate_tempest_plugin.services.dns.v2.json.ptr_client \ - import PtrClient -from designate_tempest_plugin.services.dns.v2.json.quotas_client import \ - QuotasClient -from designate_tempest_plugin.services.dns.v2.json.recordset_client import \ - RecordsetClient -from designate_tempest_plugin.services.dns.v2.json.service_client import \ - ServiceClient -from designate_tempest_plugin.services.dns.v2.json.tld_client import TldClient -from designate_tempest_plugin.services.dns.v2.json.transfer_accepts_client \ - import TransferAcceptClient -from designate_tempest_plugin.services.dns.v2.json.transfer_request_client \ - import TransferRequestClient -from designate_tempest_plugin.services.dns.v2.json.tsigkey_client import \ - TsigkeyClient -from designate_tempest_plugin.services.dns.v2.json.zones_client import \ - ZonesClient -from designate_tempest_plugin.services.dns.v2.json.zone_exports_client import \ - ZoneExportsClient -from designate_tempest_plugin.services.dns.v2.json.zone_imports_client import \ - ZoneImportsClient -from designate_tempest_plugin.services.dns.v2.json.api_version_client import \ - ApiVersionClient +from .json.blacklists_client import BlacklistsClient +from .json.designate_limit_client import DesignateLimitClient +from .json.pool_client import PoolClient +from .json.ptr_client import PtrClient +from .json.quotas_client import QuotasClient +from .json.recordset_client import RecordsetClient +from .json.service_client import ServiceClient +from .json.tld_client import TldClient +from .json.transfer_accepts_client import TransferAcceptClient +from .json.transfer_request_client import TransferRequestClient +from .json.tsigkey_client import TsigkeyClient +from .json.zones_client import ZonesClient +from .json.zone_exports_client import ZoneExportsClient +from .json.zone_imports_client import ZoneImportsClient +from .json.api_version_client import ApiVersionClient __all__ = ['BlacklistsClient', 'DesignateLimitClient', 'PoolClient', 'PtrClient', 'QuotasClient', 'RecordsetClient', 'ServiceClient', diff --git a/designate_tempest_plugin/tests/api/v2/test_zone_tasks.py b/designate_tempest_plugin/tests/api/v2/test_zone_tasks.py index 159d75e5..678eb295 100644 --- a/designate_tempest_plugin/tests/api/v2/test_zone_tasks.py +++ b/designate_tempest_plugin/tests/api/v2/test_zone_tasks.py @@ -24,8 +24,8 @@ from designate_tempest_plugin.common import waiters from designate_tempest_plugin import data_utils as dns_data_utils from designate_tempest_plugin.tests import base -from designate_tempest_plugin.services.dns.query.query_client \ - import SingleQueryClient +from designate_tempest_plugin.services.dns.query.query_client import ( + SingleQueryClient) CONF = config.CONF LOG = logging.getLogger(__name__) diff --git a/designate_tempest_plugin/tests/base.py b/designate_tempest_plugin/tests/base.py index d5cdb6b4..24156d84 100644 --- a/designate_tempest_plugin/tests/base.py +++ b/designate_tempest_plugin/tests/base.py @@ -15,8 +15,8 @@ from tempest import test from tempest import config from tempest.lib.common.utils import test_utils as utils -from designate_tempest_plugin.services.dns.query.query_client import \ - QueryClient +from designate_tempest_plugin.services.dns.query.query_client import ( + QueryClient) from designate_tempest_plugin.tests import rbac_utils diff --git a/designate_tempest_plugin/tests/scenario/v2/test_quotas.py b/designate_tempest_plugin/tests/scenario/v2/test_quotas.py index 9f126a49..2b20db5d 100644 --- a/designate_tempest_plugin/tests/scenario/v2/test_quotas.py +++ b/designate_tempest_plugin/tests/scenario/v2/test_quotas.py @@ -92,8 +92,8 @@ class QuotasV2Test(base.BaseDnsV2Test): def _reach_quota_limit( self, limit_threshold, quota_type, zone=None): attempt_number = 0 - not_raised_msg = "Failed, expected '413 over_quota' response of " \ - "type:{} wasn't received.".format(quota_type) + not_raised_msg = ("Failed, expected '413 over_quota' response of " + "type:{} wasn't received.".format(quota_type)) while attempt_number <= limit_threshold + 1: try: attempt_number += 1 @@ -136,8 +136,8 @@ class QuotasV2Test(base.BaseDnsV2Test): raised_err = str(e).replace(' ', '') if not_raised_msg in str(e): raise AssertionError(not_raised_msg) - elif "'code':413" in raised_err and \ - "'type':'over_quota'" in raised_err: + elif ("'code':413" in raised_err and + "'type':'over_quota'" in raised_err): LOG.info("OK, type':'over_quota' was raised") break else: @@ -309,10 +309,9 @@ class QuotasBoundary(base.BaseDnsV2Test, tempest.test.BaseTestCase): cls.quota_client = cls.os_system_admin.dns_v2.QuotasClient() cls.project_client = cls.os_system_admin.projects_client cls.zone_client = cls.os_system_admin.dns_v2.ZonesClient() - cls.recordset_client = \ - cls.os_system_admin.dns_v2.RecordsetClient() - cls.export_zone_client = \ - cls.os_system_admin.dns_v2.ZoneExportsClient() + cls.recordset_client = cls.os_system_admin.dns_v2.RecordsetClient() + cls.export_zone_client = ( + cls.os_system_admin.dns_v2.ZoneExportsClient()) else: cls.quota_client = cls.os_admin.dns_v2.QuotasClient() cls.project_client = cls.os_admin.projects_client diff --git a/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py b/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py index 029854a4..dcdf0b08 100644 --- a/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py +++ b/designate_tempest_plugin/tests/scenario/v2/test_recordsets.py @@ -23,8 +23,8 @@ from designate_tempest_plugin.tests import base from designate_tempest_plugin.common import constants as const from designate_tempest_plugin import data_utils as dns_data_utils from designate_tempest_plugin.common import waiters -from designate_tempest_plugin.services.dns.query.query_client \ - import SingleQueryClient +from designate_tempest_plugin.services.dns.query.query_client import ( + SingleQueryClient) LOG = logging.getLogger(__name__) diff --git a/designate_tempest_plugin/tests/scenario/v2/test_zones.py b/designate_tempest_plugin/tests/scenario/v2/test_zones.py index 4e9affc9..f3050944 100644 --- a/designate_tempest_plugin/tests/scenario/v2/test_zones.py +++ b/designate_tempest_plugin/tests/scenario/v2/test_zones.py @@ -25,8 +25,8 @@ from designate_tempest_plugin import data_utils as dns_data_utils from designate_tempest_plugin.tests import base from designate_tempest_plugin.common import constants as const from designate_tempest_plugin.common import waiters -from designate_tempest_plugin.services.dns.query.query_client \ - import SingleQueryClient +from designate_tempest_plugin.services.dns.query.query_client import ( + SingleQueryClient) CONF = config.CONF diff --git a/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py b/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py index 565461d9..67e5f325 100644 --- a/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py +++ b/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py @@ -22,8 +22,8 @@ from tempest.lib import decorators from designate_tempest_plugin.common import constants as const from designate_tempest_plugin.common import waiters from designate_tempest_plugin import data_utils as dns_data_utils -from designate_tempest_plugin.tests.api.v2.test_zones_exports import \ - BaseZoneExportsTest +from designate_tempest_plugin.tests.api.v2.test_zones_exports import ( + BaseZoneExportsTest) CONF = config.CONF LOG = logging.getLogger(__name__) diff --git a/designate_tempest_plugin/tests/scenario/v2/test_zones_import.py b/designate_tempest_plugin/tests/scenario/v2/test_zones_import.py index 7286f23d..21421588 100644 --- a/designate_tempest_plugin/tests/scenario/v2/test_zones_import.py +++ b/designate_tempest_plugin/tests/scenario/v2/test_zones_import.py @@ -17,8 +17,8 @@ from tempest.lib import decorators from designate_tempest_plugin.common import constants as const from designate_tempest_plugin.common import waiters from designate_tempest_plugin import data_utils as dns_data_utils -from designate_tempest_plugin.tests.api.v2.test_zones_imports import \ - BaseZonesImportTest +from designate_tempest_plugin.tests.api.v2.test_zones_imports import ( + BaseZonesImportTest) LOG = logging.getLogger(__name__) diff --git a/tox.ini b/tox.ini index 545cdca8..7a5148ba 100644 --- a/tox.ini +++ b/tox.ini @@ -105,3 +105,15 @@ extension = T112 = tempest.hacking.checks:dont_import_local_tempest_into_lib T113 = tempest.hacking.checks:dont_use_config_in_tempest_lib T114 = tempest.hacking.checks:use_rand_uuid_instead_of_uuid4 + D701 = checks:mutable_default_arguments + D703 = checks:check_explicit_underscore_import + D704 = checks:no_import_graduated_oslo_libraries + D705 = checks:use_timeutils_utcnow + D706 = checks:no_translate_debug_logs + D707 = checks:check_no_basestring + D708 = checks:check_python3_xrange + D709 = checks:check_no_log_audit + D710 = checks:check_no_log_warn + D711 = checks:check_line_continuation_no_backslash +paths = ./designate_tempest_plugin/hacking +