diff --git a/neutron/cmd/runtime_checks.py b/neutron/cmd/runtime_checks.py index 060e4588eb6..49c66a021a9 100644 --- a/neutron/cmd/runtime_checks.py +++ b/neutron/cmd/runtime_checks.py @@ -13,10 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. -from packaging import version - from neutron_lib import exceptions from oslo_log import log as logging +from oslo_utils import versionutils from neutron.agent.linux import utils as agent_utils @@ -46,7 +45,9 @@ def get_keepalived_version(): return_stderr=True) # First line is the interesting one here from stderr version_line = res[1].split('\n')[0] - keepalived_version = version.parse(version_line.split()[1]) + # Version string is of form 'v2.0.19', must remove 'v' + keepalived_version = versionutils.convert_version_to_tuple( + version_line.split()[1].lstrip('v')) return keepalived_version except exceptions.ProcessExecutionError: LOG.exception("Failed to get keepalived version") @@ -55,7 +56,7 @@ def get_keepalived_version(): def keepalived_use_no_track_support(): - keepalived_with_track = version.parse('2.0.3') + keepalived_with_track = (2, 0, 3) keepalived_version = get_keepalived_version() if keepalived_version: return keepalived_version >= keepalived_with_track diff --git a/neutron/cmd/sanity/checks.py b/neutron/cmd/sanity/checks.py index 6ab2f42b0f9..7c942fee178 100644 --- a/neutron/cmd/sanity/checks.py +++ b/neutron/cmd/sanity/checks.py @@ -450,7 +450,7 @@ def keepalived_ipv6_supported(): def keepalived_garp_on_sighup_supported(): - keepalived_garp_on_sighup = ('1.2.20') + keepalived_garp_on_sighup = (1, 2, 20) keepalived_version = runtime_checks.get_keepalived_version() if keepalived_version: return keepalived_version >= keepalived_garp_on_sighup diff --git a/neutron/hacking/checks.py b/neutron/hacking/checks.py index ffdcd71935b..82144abc6e9 100644 --- a/neutron/hacking/checks.py +++ b/neutron/hacking/checks.py @@ -42,6 +42,10 @@ import_from_mock = re.compile(r"\bfrom[\s]+mock[\s]+import\b") import_six = re.compile(r"\bimport[\s]+six\b") import_from_six = re.compile(r"\bfrom[\s]+six[\s]+import\b") +import_packaging = re.compile(r"\bimport[\s]+packaging\b") +import_version_from_packaging = ( + re.compile(r"\bfrom[\s]+packaging[\s]+import[\s]version\b")) + @core.flake8ext def check_assert_called_once_with(logical_line, filename): @@ -264,3 +268,17 @@ def check_no_import_six(logical_line, filename, noqa): for regex in import_six, import_from_six: if re.match(regex, logical_line): yield(0, msg) + + +@core.flake8ext +def check_no_import_packaging(logical_line, filename, noqa): + """N349 - Code must not import packaging library + """ + msg = "N349: Code must not import packaging library" + + if noqa: + return + + for regex in import_packaging, import_version_from_packaging: + if re.match(regex, logical_line): + yield(0, msg) diff --git a/neutron/tests/common/helpers.py b/neutron/tests/common/helpers.py index 30130c97e8b..e6ff9e6f199 100644 --- a/neutron/tests/common/helpers.py +++ b/neutron/tests/common/helpers.py @@ -23,7 +23,7 @@ from neutron_lib.agent import topics from neutron_lib import constants from neutron_lib import context from oslo_utils import timeutils -from packaging import version +from oslo_utils import versionutils import neutron from neutron.agent.common import ovs_lib @@ -223,8 +223,11 @@ def skip_if_ovs_older_than(ovs_version): @functools.wraps(f) def check_ovs_and_skip(test): ovs = ovs_lib.BaseOVS() - current_ovs_version = version.Version(ovs.config['ovs_version']) - if current_ovs_version < version.Version(ovs_version): + current_ovs_version = versionutils.convert_version_to_tuple( + ovs.config['ovs_version']) + min_ovs_version = versionutils.convert_version_to_tuple( + ovs_version) + if current_ovs_version < min_ovs_version: test.skipTest("This test requires OVS version %s or higher." % ovs_version) return f(test) diff --git a/neutron/tests/unit/cmd/test_runtime_checks.py b/neutron/tests/unit/cmd/test_runtime_checks.py new file mode 100644 index 00000000000..0bd3ad2e0aa --- /dev/null +++ b/neutron/tests/unit/cmd/test_runtime_checks.py @@ -0,0 +1,39 @@ +# Copyright 2023 Canonical +# 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 unittest import mock + +from neutron_lib import exceptions + +from neutron.agent.linux import utils as agent_utils +from neutron.cmd import runtime_checks +from neutron.tests import base + + +class TestRuntimeChecks(base.BaseTestCase): + + def test_get_keepalived_version(self): + exec_keepalived_version = ['', 'Keepalived v2.2.8 (04/04,2023)'] + with mock.patch.object(agent_utils, 'execute') as mock_exec: + mock_exec.return_value = exec_keepalived_version + keepalived_version = runtime_checks.get_keepalived_version() + self.assertEqual(keepalived_version, (2, 2, 8)) + + def test_get_keepalived_version_fail(self): + with mock.patch.object(agent_utils, 'execute') as mock_exec: + mock_exec.side_effect = ( + exceptions.ProcessExecutionError('', returncode=0)) + keepalived_version = runtime_checks.get_keepalived_version() + self.assertFalse(keepalived_version) diff --git a/neutron/tests/unit/hacking/test_checks.py b/neutron/tests/unit/hacking/test_checks.py index dba4fb041fe..79fa8818571 100644 --- a/neutron/tests/unit/hacking/test_checks.py +++ b/neutron/tests/unit/hacking/test_checks.py @@ -238,6 +238,19 @@ class HackingTestCase(base.BaseTestCase): 1, len(list(checks.check_no_import_six( fail_line, mock.ANY, None)))) + def test_check_no_import_packaging(self): + pass_line = 'import other_library import packaging' + fail_lines = ('import packaging', + 'from packaging import version') + self.assertEqual( + 0, + len(list(checks.check_no_import_packaging(pass_line, mock.ANY, + None)))) + for fail_line in fail_lines: + self.assertEqual( + 1, len(list(checks.check_no_import_packaging( + fail_line, mock.ANY, None)))) + def test_check_oslo_i18n_wrapper(self): def _pass(line, filename, noqa=False): self.assertLinePasses( diff --git a/requirements.txt b/requirements.txt index 7e208c38f39..18f8cdad927 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,7 +51,6 @@ os-ken>=2.2.0 # Apache-2.0 os-resource-classes>=1.1.0 # Apache-2.0 ovs>=2.10.0 # Apache-2.0 ovsdbapp>=2.2.1 # Apache-2.0 -packaging>=20.4 # Apache-2.0 psutil>=5.3.0 # BSD pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) pyOpenSSL>=17.1.0 # Apache-2.0 diff --git a/tox.ini b/tox.ini index d32ed8bf307..0b600ad7e77 100644 --- a/tox.ini +++ b/tox.ini @@ -211,6 +211,7 @@ extension = N346 = neutron.hacking.checks:check_no_sqlalchemy_event_import N347 = neutron.hacking.checks:check_no_import_mock N348 = neutron.hacking.checks:check_no_import_six + N349 = neutron.hacking.checks:check_no_import_packaging # Checks from neutron-lib N521 = neutron_lib.hacking.checks:use_jsonutils N524 = neutron_lib.hacking.checks:check_no_contextlib_nested