Skip to content

Commit

Permalink
Add DistroVersion class to compare distro versions (#3078)
Browse files Browse the repository at this point in the history
* Add DistroVersion class to compare distro versions

* comment

* python 2

---------

Co-authored-by: narrieta <narrieta>
  • Loading branch information
narrieta authored Mar 6, 2024
1 parent 68b77af commit 3e4fb9a
Show file tree
Hide file tree
Showing 13 changed files with 1,769 additions and 292 deletions.
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

0 comments on commit 3e4fb9a

Please sign in to comment.