diff --git a/.zuul.yaml b/.zuul.yaml index bb18e312..4bcb0ae8 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -10,15 +10,21 @@ jobs: - openstack-tox-linters - openstack-tox-pep8 + - fault-tox-pylint - stx-fault-build - fault-rest-api-py27 - - fault-rest-api-py35 + - fault-rest-api-py36 gate: jobs: - openstack-tox-linters - openstack-tox-pep8 + - fault-tox-pylint - fault-rest-api-py27 - - fault-rest-api-py35 + - fault-rest-api-py36 + post: + jobs: + - stx-fault-upload-git-mirror + # Perform just a build - job: @@ -58,7 +64,7 @@ parent: tox description: | Run py27 test for fm-rest-api - nodeset: ubuntu-xenial + nodeset: ubuntu-bionic required-projects: - starlingx/config vars: @@ -66,13 +72,115 @@ tox_extra_args: -c fm-rest-api/fm/tox.ini - job: - name: fault-rest-api-py35 + name: fault-rest-api-py36 parent: tox description: | - Run py35 test for fm-rest-api - nodeset: ubuntu-xenial + Run py36 test for fm-rest-api + nodeset: ubuntu-bionic required-projects: - starlingx/config vars: - tox_envlist: py35 + tox_envlist: py36 tox_extra_args: -c fm-rest-api/fm/tox.ini + +- job: + name: fault-tox-pylint + parent: tox + description: | + Run pylint for python files in fault + required-projects: + - starlingx/config + vars: + tox_envlist: pylint + +- job: + name: stx-fault-upload-git-mirror + parent: upload-git-mirror + description: > + Mirrors opendev.org/starlingx/fault to + github.com/starlingx/fault + vars: + git_mirror_repository: starlingx/fault + secrets: + - name: git_mirror_credentials + secret: stx-fault-github-secret + pass-to-parent: true + +- secret: + name: stx-fault-github-secret + data: + user: git + host: github.com + # yamllint disable-line rule:line-length + host_key: github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + ssh_key: !encrypted/pkcs1-oaep + - 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 diff --git a/fm-api/fm_api/fm_api.py b/fm-api/fm_api/fm_api.py index d2cd34d8..0da0b7b6 100755 --- a/fm-api/fm_api/fm_api.py +++ b/fm-api/fm_api/fm_api.py @@ -13,7 +13,7 @@ import copy from . import constants import six -import fm_core +import fm_core # pylint: disable=import-error import threading fm_api_lock = threading.Lock() diff --git a/fm-rest-api/fm/.coveragerc b/fm-rest-api/fm/.coveragerc new file mode 100644 index 00000000..8fa459cc --- /dev/null +++ b/fm-rest-api/fm/.coveragerc @@ -0,0 +1,8 @@ +[run] +branch = True +source = fm +omit = fm/tests/* + +[report] +ignore_errors = True + diff --git a/fm-rest-api/fm/fm/tests/api/__init__.py b/fm-rest-api/fm/fm/tests/api/__init__.py new file mode 100644 index 00000000..70618e64 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/api/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. diff --git a/fm-rest-api/fm/fm/tests/api/base.py b/fm-rest-api/fm/fm/tests/api/base.py new file mode 100644 index 00000000..d8506099 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/api/base.py @@ -0,0 +1,79 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. +"""Base classes for API tests.""" + +from oslo_config import cfg +import pecan +import pecan.testing + +from fm.tests import base +from fm.common import context as fm_context + + +PATH_PREFIX = '/v1' + + +class FunctionalTest(base.TestCase): + """Used for functional tests of Pecan controllers where you need to + test your literal application and its integration with the + framework. + """ + + SOURCE_DATA = {'test_source': {'somekey': '666'}} + + def setUp(self): + super(FunctionalTest, self).setUp() + self.context = fm_context.RequestContext(is_admin=True) + self.app = self._make_app() + + def _make_app(self): + cfg.CONF.set_override("debug", True) + + self.config = { + 'app': { + 'root': 'fm.api.controllers.root.RootController', + 'modules': ['fm.api'], + 'acl_public_routes': ['/', '/v1'], + }, + } + + return pecan.testing.load_test_app(self.config) + + def tearDown(self): + super(FunctionalTest, self).tearDown() + pecan.set_config({}, overwrite=True) + + def get_json(self, path, expect_errors=False, headers=None, + extra_environ=None, q=[], path_prefix=PATH_PREFIX, **params): + full_path = path_prefix + path + query_params = {'q.field': [], + 'q.value': [], + 'q.op': [], + } + for query in q: + for name in ['field', 'op', 'value']: + query_params['q.%s' % name].append(query.get(name, '')) + all_params = {} + all_params.update(params) + if q: + all_params.update(query_params) + response = self.app.get(full_path, + params=all_params, + headers=headers, + extra_environ=extra_environ, + expect_errors=expect_errors) + if not expect_errors: + response = response.json + return response diff --git a/fm-rest-api/fm/fm/tests/api/test_base.py b/fm-rest-api/fm/fm/tests/api/test_base.py new file mode 100644 index 00000000..179ea53a --- /dev/null +++ b/fm-rest-api/fm/fm/tests/api/test_base.py @@ -0,0 +1,30 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. + +from fm.tests.api import base + + +class TestBase(base.FunctionalTest): + + def test_api_setup(self): + pass + + def test_bad_uri(self): + response = self.get_json('/bad/path', + expect_errors=True, + headers={"Accept": "application/json"}) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) diff --git a/fm-rest-api/fm/fm/tests/api/test_root.py b/fm-rest-api/fm/fm/tests/api/test_root.py new file mode 100644 index 00000000..802e41ae --- /dev/null +++ b/fm-rest-api/fm/fm/tests/api/test_root.py @@ -0,0 +1,40 @@ +# Copyright 2013 Red Hat, Inc. +# All Rights Reserved. +# +# 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. +# +# Copyright 2020 Intel Corporation. +# + +from fm.tests.api import base + + +class TestRoot(base.FunctionalTest): + + def test_get_root(self): + data = self.get_json('/', path_prefix='') + self.assertEqual(data['default_version']['id'], 'v1') + # Check fields are not empty + [self.assertNotIn(f, ['', []]) for f in data.keys()] + + +class TestV1Root(base.FunctionalTest): + + def test_get_v1_root(self): + data = self.get_json('/') + self.assertEqual(data['id'], 'v1') + # Check fields are not empty + [self.assertNotIn(f, ['', []]) for f in data.keys()] + # Check if the resources are present + self.assertIn({'type': 'application/vnd.openstack.fm.v1+json', + 'base': 'application/json'}, data['media_types']) diff --git a/fm-rest-api/fm/fm/tests/base.py b/fm-rest-api/fm/fm/tests/base.py index 9a8d4678..7c7cf4fa 100644 --- a/fm-rest-api/fm/fm/tests/base.py +++ b/fm-rest-api/fm/fm/tests/base.py @@ -19,13 +19,24 @@ Allows overriding of config for use of fakes, and some black magic for inline callbacks. """ +import sys +import os + import fixtures +import mock import testtools from oslo_config import cfg +from oslo_db.sqlalchemy import enginefacade from oslo_log import log as logging +from fm.db import migration +from fm.tests import conf_fixture + CONF = cfg.CONF +_DB_CACHE = None + +sys.modules['fm_core'] = mock.Mock() class TestCase(testtools.TestCase): @@ -35,12 +46,44 @@ class TestCase(testtools.TestCase): """Run before each test method to initialize test environment.""" super(TestCase, self).setUp() + test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0) + try: + test_timeout = int(test_timeout) + except ValueError: + # If timeout value is invalid do not set a timeout. + test_timeout = 0 + if test_timeout > 0: + self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) + self.useFixture(fixtures.NestedTempfile()) + self.useFixture(fixtures.TempHomeDir()) + + if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or + os.environ.get('OS_STDOUT_CAPTURE') == '1'): + stdout = self.useFixture(fixtures.StringStream('stdout')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) + if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or + os.environ.get('OS_STDERR_CAPTURE') == '1'): + stderr = self.useFixture(fixtures.StringStream('stderr')).stream + self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) + + self.log_fixture = self.useFixture(fixtures.FakeLogger()) + def fake_logging_setup(*args): pass self.useFixture( fixtures.MonkeyPatch('oslo_log.log.setup', fake_logging_setup)) + logging.register_options(CONF) + self.useFixture(conf_fixture.ConfFixture(CONF)) + + global _DB_CACHE + if not _DB_CACHE: + engine = enginefacade.get_legacy_facade().get_engine() + engine.dispose() + engine.connect() + migration.db_sync(engine=engine) + def tearDown(self): super(TestCase, self).tearDown() diff --git a/fm-rest-api/fm/fm/tests/conf_fixture.py b/fm-rest-api/fm/fm/tests/conf_fixture.py new file mode 100644 index 00000000..83f2e809 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/conf_fixture.py @@ -0,0 +1,35 @@ +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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. +# +# Copyright 2020 Intel Corporation. + +from oslo_config import cfg +from oslo_config import fixture as config_fixture + +CONF = cfg.CONF + + +class ConfFixture(config_fixture.Config): + """Fixture to manage global conf settings.""" + + def __init__(self, conf): + self.conf = conf + + def setUp(self): + super(ConfFixture, self).setUp() + + self.conf.set_default('connection', "sqlite://", group='database') + self.addCleanup(self.conf.reset) diff --git a/fm-rest-api/fm/fm/tests/db/__init__.py b/fm-rest-api/fm/fm/tests/db/__init__.py new file mode 100644 index 00000000..70618e64 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/db/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. diff --git a/fm-rest-api/fm/fm/tests/db/base.py b/fm-rest-api/fm/fm/tests/db/base.py new file mode 100644 index 00000000..2315eff6 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/db/base.py @@ -0,0 +1,32 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. + +"""Fault DB test base class.""" + +import abc +import six + +from fm.common import context +from fm.tests import base + +INIT_VERSION = 0 + + +@six.add_metaclass(abc.ABCMeta) +class DbTestCase(base.TestCase): + + def setUp(self): + super(DbTestCase, self).setUp() + self.admin_context = context.make_context(is_admin=True) diff --git a/fm-rest-api/fm/fm/tests/db/test_alarm.py b/fm-rest-api/fm/fm/tests/db/test_alarm.py new file mode 100644 index 00000000..3c64c471 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/db/test_alarm.py @@ -0,0 +1,24 @@ +# Copyright 2020 Intel Corporation. +# +# SPDX-License-Identifier: Apache-2.0 +# + + +"""Tests for Alarm via the DB API""" + +from fm.db import api as dbapi +from fm.tests.db import base +from fm.tests.db import utils + + +class DbAlarmTestCase(base.DbTestCase): + + def setUp(self): + super(DbAlarmTestCase, self).setUp() + self.dbapi = dbapi.get_instance() + + def test_create_alarm(self): + uuid = 1234567 + alarm = utils.get_test_alarm(uuid=uuid) + alarm_exist = self.dbapi.alarm_create(alarm) + self.assertEqual(uuid, alarm_exist.uuid) diff --git a/fm-rest-api/fm/fm/tests/db/utils.py b/fm-rest-api/fm/fm/tests/db/utils.py new file mode 100644 index 00000000..4df03dc2 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/db/utils.py @@ -0,0 +1,52 @@ +# All Rights Reserved. +# +# 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. +# +# Copyright 2020 Intel Corporation + +"""Fault test utilities.""" + +from fm.db import api as db_api +from fm_api import constants + + +def get_test_alarm(**kw): + alarm = { + 'uuid': kw.get('uuid'), + 'alarm_id': kw.get('alarm_id', constants.FM_ALARM_ID_VM_FAILED), + 'alarm_state': kw.get('alarm_state', constants.FM_ALARM_STATE_SET), + 'entity_type_id': kw.get('entity_type_id', constants.FM_ENTITY_TYPE_INSTANCE), + 'entity_instance_id': kw.get('entity_instance_id', + constants.FM_ENTITY_TYPE_INSTANCE + '=' + + 'a4e4cdb7-2ee6-4818-84c8-5310fcd67b5d'), + 'severity': kw.get('severity', constants.FM_ALARM_SEVERITY_CRITICAL), + 'reason_text': kw.get('reason_text', "Unknown"), + 'alarm_type': kw.get('alarm_type', constants.FM_ALARM_TYPE_5), + 'probable_cause': kw.get('probable_cause', constants.ALARM_PROBABLE_CAUSE_8), + 'proposed_repair_action': None, + 'service_affecting': False, + 'suppression': False + } + return alarm + + +def create_test_alarm(**kw): + """Create test alarm entry in DB and return alarm DB object. + Function to be used to create test alarm objects in the database. + :param kw: kwargs with overriding values for alarm's attributes. + :returns: Test alarm DB object. + """ + alarm = get_test_alarm(**kw) + # Let DB generate ID if it isn't specified explicitly + dbapi = db_api.get_instance() + return dbapi.alarm_create(alarm) diff --git a/fm-rest-api/fm/fm/tests/test_dbsync.py b/fm-rest-api/fm/fm/tests/test_dbsync.py new file mode 100644 index 00000000..95f63d49 --- /dev/null +++ b/fm-rest-api/fm/fm/tests/test_dbsync.py @@ -0,0 +1,29 @@ +# Copyright 2020 Intel Corporation. +# All Rights Reserved. +# +# 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. + +from fm.db import migration +from fm.db.sqlalchemy import api as db_api +from fm.tests.db import base + + +class DbSyncTestCase(base.DbTestCase): + def setUp(self): + super(DbSyncTestCase, self).setUp() + + def test_sync_and_version(self): + migration.db_sync() + engine = db_api.get_engine() + v = migration.get_backend().db_version(engine, migration.MIGRATE_REPO_PATH, None) + self.assertTrue(v > base.INIT_VERSION) diff --git a/fm-rest-api/fm/test-requirements.txt b/fm-rest-api/fm/test-requirements.txt index 02dc0100..00adf991 100644 --- a/fm-rest-api/fm/test-requirements.txt +++ b/fm-rest-api/fm/test-requirements.txt @@ -2,12 +2,24 @@ hacking!=0.13.0,<0.14,>=0.12.0 bashate >= 0.2 PyYAML >= 3.1.0 yamllint >= 0.5.2 -stestr +stestr != 3.0.0 testtools!=1.2.0,>=0.9.36 iso8601 -stestr mock cython oslo.log -oslo.concurrency - +oslo.i18n # Apache-2.0 +oslo.config>=3.7.0 # Apache-2.0 +oslo.concurrency>=3.7.1 # Apache-2.0 +oslo.db>=4.1.0 # Apache-2.0 +oslo.service>=1.10.0 # Apache-2.0 +oslo.utils>=3.5.0 # Apache-2.0 +oslo.serialization>=1.10.0,!=2.19.1 # Apache-2.0 +oslo_policy +oslo_versionedobjects +python-keystoneclient>=3.8.0 # Apache-2.0 +keystonemiddleware>=4.12.0 # Apache-2.0 +pecan>=1.0.0 +WSME>=0.5b2 +httplib2 +keyring <= 18.0.1 diff --git a/fm-rest-api/fm/tox.ini b/fm-rest-api/fm/tox.ini index e8a7991c..f883d69e 100644 --- a/fm-rest-api/fm/tox.ini +++ b/fm-rest-api/fm/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py35 +envlist = py27,py36 minversion = 2.3 skipsdist = True stxdir = {toxinidir}/../../../ @@ -12,6 +12,9 @@ setenv = VIRTUAL_ENV={envdir} OS_TEST_TIMEOUT=60 deps = -r{toxinidir}/test-requirements.txt -e{[tox]stxdir}/config/tsconfig/tsconfig + -e{[tox]stxdir}/config/sysinv/cgts-client/cgts-client + -e{[tox]stxdir}/fault/fm-api + -e{[tox]stxdir}/fault/fm-rest-api/fm [testenv:venv] basepython = python3 @@ -23,12 +26,22 @@ commands = stestr run {posargs} stestr slowest -[testenv:py35] -basepython = python3.5 +[testenv:py36] +basepython = python3.6 commands = stestr run {posargs} stestr slowest +[testenv:cover] +deps = {[testenv]deps} + coverage +setenv = {[testenv]setenv} + PYTHON=coverage run --parallel-mode - - +commands = + coverage erase + stestr run {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report diff --git a/pylint.rc b/pylint.rc new file mode 100755 index 00000000..9acd1ce2 --- /dev/null +++ b/pylint.rc @@ -0,0 +1,267 @@ +[MASTER] +# Specify a configuration file. +rcfile=pylint.rc + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. Should be base names, not paths. +ignore= + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist=lxml.etree,greenlet + + + +[MESSAGES CONTROL] +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). +# See "Messages Control" section of +# https://pylint.readthedocs.io/en/latest/user_guide +# We are disabling (C)onvention +# We are disabling (R)efactor +# We are selectively disabling (W)arning +# W0102 dangerous-default-value +# W0106 expression-not-assigned +# W0107 unnecessary-pass +# W0110 deprecated-lambda +# W0201 attribute-defined-outside-init +# W0212 protected-access +# W0221 arguments-differ +# W0223 abstract-method +# W0231 super-init-not-called +# W0235 useless-super-delegation +# W0311 bad-indentation +# W0403 relative-import (this needs to be fixed in py3) +# W0603 global-statement +# W0612 unused-variable +# W0613 unused-argument +# W0621 redefined-outer-name +# W0622 redefined-builtin +# W0703 broad-except +# W1401 anomalous-backslash-in-string +# E are error codes +# E0604 invalid-all-object +# E1101 no-member +# E1102 not-callable +# E1120 no-value-for-parameter +# E1121 too-many-function-args +disable=C, R, fixme, + W0102,W0106,W0107,W0110,W0201,W0212,W0221,W0223,W0231,W0235, + W0311,W0403,W0603,W0612,W0613,W0621,W0622,W0703,W1401, + E0604,E1101,E1102,E1120,E1121 + +[REPORTS] +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + + +[SIMILARITIES] +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +[FORMAT] +# Maximum number of characters on a single line. +max-line-length=85 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually 4 spaces or "\t" (1 tab). +indent-string=' ' + + +[TYPECHECK] +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis +ignored-modules=distutils,eventlet.green.subprocess,six,six.moves + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). +# pylint is confused by sqlalchemy Table, as well as sqlalchemy Enum types +# ie: (unprovisioned, identity) +# LookupDict in requests library confuses pylint +ignored-classes=SQLObject, optparse.Values, thread._local, _thread._local, + Table, unprovisioned, identity, LookupDict + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E0201 when accessed. Python regular +# expressions are accepted. +generated-members=REQUEST,acl_users,aq_parent + + +[BASIC] +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + + +[MISCELLANEOUS] +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the beginning of the name of dummy variables +# (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +[IMPORTS] +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[DESIGN] +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +[CLASSES] +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + + +[EXCEPTIONS] +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..ccc829ca --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +httplib2 +keystoneauth1 +keystonemiddleware +oslo.config +oslo.db +oslo.log +oslo.policy +oslo.service +oslo.utils +oslo.versionedobjects +pecan +prettytable +pyOpenSSL +SQLAlchemy +WSME diff --git a/test-requirements.txt b/test-requirements.txt index 21c49e2f..8f1b9382 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,5 +1,6 @@ hacking!=0.13.0,<0.14,>=0.12.0 bashate >= 0.2 +mock PyYAML >= 3.1.0 yamllint >= 0.5.2 #spec_cleaner>=1.0.9 diff --git a/tox.ini b/tox.ini index 875d9873..17cf525c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,13 @@ [tox] -envlist = linters,pep8,rpm-packaging-lint +envlist = linters,pep8,pylint,rpm-packaging-lint minversion = 2.3 skipsdist = True stxdir = {toxinidir}/../ [testenv] -install_command = pip install -U {opts} {packages} +install_command = pip install \ + -chttps://opendev.org/openstack/requirements/raw/branch/stable/stein/upper-constraints.txt \ + {opts} {packages} setenv = VIRTUAL_ENV={envdir} OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 @@ -17,9 +19,6 @@ basepython = python3 setenv = VIRTUAL_ENV={envdir} LC_ALL=en_US.utf-8 -install_command = pip install -U \ - -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/stable/stein/upper-constraints.txt} \ - {opts} {packages} deps = -r{toxinidir}/test-requirements.txt whitelist_externals = bash commands = @@ -51,6 +50,23 @@ commands = -o -type f -name '*.yaml' \ -print0 | xargs -0 yamllint -d '\{extends: relaxed, rules: \{line-length: \{max: 260\}\}\}'" +[testenv:pylint] +basepython = python2.7 +sitepackages = False + +deps = {[testenv]deps} + -e{toxinidir}/../config/tsconfig/tsconfig + -e{toxinidir}/../config/sysinv/cgts-client/cgts-client + -r{toxinidir}/requirements.txt + pylint + +commands = pylint {posargs} --rcfile=./pylint.rc \ + fm-api/fm_api \ + fm-common/sources/fm_db_sync_event_suppression.py \ + fm-rest-api/fm/fm \ + python-fmclient/fmclient/fmclient + + #### # Add flake8 as pep8 codestyle check. [testenv:pep8]