Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DistroVersion class to compare distro versions #3078

Merged
merged 5 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions azurelinuxagent/common/future.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,6 @@
else:
raise ImportError("Unknown python version: {0}".format(sys.version_info))

#
# distutils has been removed from Python >= 3.12; use the copy from azurelinuxagent instead
#
if sys.version_info[0] == 3 and sys.version_info[1] >= 12:
from azurelinuxagent.distutils import version
else:
from distutils import version # pylint: disable=deprecated-module
Version = version.Version
LooseVersion = version.LooseVersion


def get_linux_distribution(get_full_name, supported_dists):
"""Abstract platform.linux_distribution() call which is deprecated as of
Expand Down
26 changes: 12 additions & 14 deletions azurelinuxagent/common/osutil/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
#


from azurelinuxagent.common.future import LooseVersion as Version

import azurelinuxagent.common.logger as logger
from azurelinuxagent.common.version import DISTRO_NAME, DISTRO_CODE_NAME, DISTRO_VERSION, DISTRO_FULL_NAME
from azurelinuxagent.common.utils.distro_version import DistroVersion
from .alpine import AlpineOSUtil
from .arch import ArchUtil
from .bigip import BigIpOSUtil
Expand Down Expand Up @@ -66,14 +65,14 @@ def _get_osutil(distro_name, distro_code_name, distro_version, distro_full_name)
return ClearLinuxUtil()

if distro_name == "ubuntu":
ubuntu_version = Version(distro_version)
if ubuntu_version in [Version("12.04"), Version("12.10")]:
ubuntu_version = DistroVersion(distro_version)
if ubuntu_version in [DistroVersion("12.04"), DistroVersion("12.10")]:
return Ubuntu12OSUtil()
if ubuntu_version in [Version("14.04"), Version("14.10")]:
if ubuntu_version in [DistroVersion("14.04"), DistroVersion("14.10")]:
return Ubuntu14OSUtil()
if ubuntu_version in [Version('16.04'), Version('16.10'), Version('17.04')]:
if ubuntu_version in [DistroVersion('16.04'), DistroVersion('16.10'), DistroVersion('17.04')]:
return Ubuntu16OSUtil()
if Version('18.04') <= ubuntu_version <= Version('24.04'):
if DistroVersion('18.04') <= ubuntu_version <= DistroVersion('24.04'):
return Ubuntu18OSUtil()
if distro_full_name == "Snappy Ubuntu Core":
return UbuntuSnappyOSUtil()
Expand All @@ -91,14 +90,14 @@ def _get_osutil(distro_name, distro_code_name, distro_version, distro_full_name)

if distro_name in ("suse", "sle-micro", "sle_hpc", "sles", "opensuse"):
if distro_full_name == 'SUSE Linux Enterprise Server' \
and Version(distro_version) < Version('12') \
or distro_full_name == 'openSUSE' and Version(distro_version) < Version('13.2'):
and DistroVersion(distro_version) < DistroVersion('12') \
or distro_full_name == 'openSUSE' and DistroVersion(distro_version) < DistroVersion('13.2'):
return SUSE11OSUtil()

return SUSEOSUtil()

if distro_name == "debian":
if "sid" in distro_version or Version(distro_version) > Version("7"):
if "sid" in distro_version or DistroVersion(distro_version) > DistroVersion("7"):
return DebianOSModernUtil()

return DebianOSBaseUtil()
Expand All @@ -109,16 +108,15 @@ def _get_osutil(distro_name, distro_code_name, distro_version, distro_full_name)
# to distinguish between debian and devuan. The new distro.linux_distribution module
# is able to distinguish between the two.

if distro_name == "devuan" and Version(distro_version) >= Version("4"):
if distro_name == "devuan" and DistroVersion(distro_version) >= DistroVersion("4"):
return DevuanOSUtil()


if distro_name in ("redhat", "rhel", "centos", "oracle", "almalinux",
"cloudlinux", "rocky"):
if Version(distro_version) < Version("7"):
if DistroVersion(distro_version) < DistroVersion("7"):
return Redhat6xOSUtil()

if Version(distro_version) >= Version("8.6"):
if DistroVersion(distro_version) >= DistroVersion("8.6"):
return RedhatOSModernUtil()

return RedhatOSUtil()
Expand Down
115 changes: 115 additions & 0 deletions azurelinuxagent/common/utils/distro_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Microsoft Azure Linux Agent
#
# Copyright 2020 Microsoft Corporation
#
# 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.
#
# Requires Python 2.6+ and Openssl 1.0+
#

"""
"""

import re


class DistroVersion(object):
"""
Distro versions (as exposed by azurelinuxagent.common.version.DISTRO_VERSION) can be very arbitrary:

9.2.0
0.0.0.0_99496
10.0_RC2
1.4-rolling-202402090309
2015.11-git
2023
2023.02.1
2.1-systemd-rc1
2308a
3.11.2-dev20240212t1512utc-autotag
3.11.2-rc.1
3.1.22-1.8
8.1.3-p1-24838
8.1.3-p8-khilan.unadkat-08415223c9a99546b566df0dbc683ffa378cfd77
9.13.1P8X1
9.13.1RC1
9.2.0-beta1-25971
a
ArrayOS
bookworm/sid
Clawhammer__9.14.0
FFFF
h
JNPR-11.0-20200922.4042921_build
lighthouse-23.10.0
Lighthouse__9.13.1
linux-os-31700
Mightysquirrel__9.15.0
n/a
NAME="SLES"
ngfw-6.10.13.26655.fips.2
r11427-9ce6aa9d8d
SonicOSX 7.1.1-7047-R3003-HF24239
unstable
vsbc-x86_pi3-6.10.3
vsbc-x86_pi3-6.12.2pre02

The DistroVersion allows to compare these versions following an strategy similar to the now deprecated distutils.LooseVersion:
versions consist of a series of sequences of numbers, alphabetic characters, or any other characters, optionally separated dots
(the dots themselves are stripped out). When comparing versions the numeric components are compared numerically, while the
other components are compared lexicographically.

NOTE: For entities with simpler version schemes (e.g. extensions and the Agent), use FlexibleVersion.

"""
def __init__(self, version):
self._version = version
self._fragments = [
int(x) if DistroVersion._number_re.match(x) else x
for x in DistroVersion._fragment_re.split(self._version) if x != '' and x != '.'
]

_fragment_re = re.compile(r'(\d+|[a-z]+|\.)', re.IGNORECASE)

_number_re = re.compile(r'\d+')

def __str__(self):
return self._version

def __repr__(self):
return str(self)

def __eq__(self, other):
return self._compare(other) == 0

def __lt__(self, other):
return self._compare(other) < 0

def __le__(self, other):
return self._compare(other) <= 0

def __gt__(self, other):
return self._compare(other) > 0

def __ge__(self, other):
return self._compare(other) >= 0

def _compare(self, other):
if isinstance(other, str):
other = DistroVersion(other)

if self._fragments < other._fragments:
return -1
if self._fragments > other._fragments:
return 1
return 0
11 changes: 6 additions & 5 deletions azurelinuxagent/common/utils/flexible_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
# Requires Python 2.6+ and Openssl 1.0+
#

from azurelinuxagent.common.future import Version
import re


class FlexibleVersion(Version):
class FlexibleVersion(object):
"""
A more flexible implementation of distutils.version.StrictVersion
A more flexible implementation of distutils.version.StrictVersion.

NOTE: Use this class for generic version comparisons, e.g. extension and Agent
versions. Distro versions can be very arbitrary and should be handled
using the DistroVersion class.

The implementation allows to specify:
- an arbitrary number of version numbers:
Expand All @@ -41,8 +44,6 @@ class FlexibleVersion(Version):
"""

def __init__(self, vstring=None, sep='.', prerel_tags=('alpha', 'beta', 'rc')):
Version.__init__(self)

if sep is None:
sep = '.'
if prerel_tags is None:
Expand Down
Empty file.
Loading
Loading