blob: 7983474538fca46db78f8c503bb73e77dfe2a9df [file] [log] [blame]
#
#
# Copyright (C) 2013 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Version utilities."""
import re
from ganeti import constants
_FULL_VERSION_RE = re.compile(r"(\d+)\.(\d+)\.(\d+)")
_SHORT_VERSION_RE = re.compile(r"(\d+)\.(\d+)")
# The first Ganeti version that supports automatic upgrades
FIRST_UPGRADE_VERSION = (2, 10, 0)
CURRENT_VERSION = (constants.VERSION_MAJOR, constants.VERSION_MINOR,
constants.VERSION_REVISION)
# Format for CONFIG_VERSION:
# 01 03 0123 = 01030123
# ^^ ^^ ^^^^
# | | + Configuration version/revision
# | + Minor version
# + Major version
#
# It is stored as an integer. Make sure not to write an octal number.
# BuildVersion and SplitVersion must be in here because we can't import other
# modules. The cfgupgrade tool must be able to read and write version numbers
# and thus requires these functions. To avoid code duplication, they're kept in
# here.
def BuildVersion(major, minor, revision):
"""Calculates int version number from major, minor and revision numbers.
Returns: int representing version number
"""
assert isinstance(major, int)
assert isinstance(minor, int)
assert isinstance(revision, int)
return (1000000 * major +
10000 * minor +
1 * revision)
def SplitVersion(version):
"""Splits version number stored in an int.
Returns: tuple; (major, minor, revision)
"""
assert isinstance(version, int)
(major, remainder) = divmod(version, 1000000)
(minor, revision) = divmod(remainder, 10000)
return (major, minor, revision)
def ParseVersion(versionstring):
"""Parses a version string.
@param versionstring: the version string to parse
@type versionstring: string
@rtype: tuple or None
@return: (major, minor, revision) if parsable, None otherwise.
"""
m = _FULL_VERSION_RE.match(versionstring)
if m is not None:
return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
m = _SHORT_VERSION_RE.match(versionstring)
if m is not None:
return (int(m.group(1)), int(m.group(2)), 0)
return None
def UpgradeRange(target, current=CURRENT_VERSION):
"""Verify whether a version is within the range of automatic upgrades.
@param target: The version to upgrade to as (major, minor, revision)
@type target: tuple
@param current: The version to upgrade from as (major, minor, revision)
@type current: tuple
@rtype: string or None
@return: None, if within the range, and a human-readable error message
otherwise
"""
if target < FIRST_UPGRADE_VERSION or current < FIRST_UPGRADE_VERSION:
return "automatic upgrades only supported from 2.10 onwards"
if target[0] != current[0]:
return "different major versions"
if target[1] < current[1] - 1:
return "can only downgrade one minor version at a time"
return None
def ShouldCfgdowngrade(version, current=CURRENT_VERSION):
"""Decide whether cfgupgrade --downgrade should be called.
Given the current version and the version to change to, decide
if in the transition process cfgupgrade --downgrade should
be called
@param version: The version to upgrade to as (major, minor, revision)
@type version: tuple
@param current: The version to upgrade from as (major, minor, revision)
@type current: tuple
@rtype: bool
@return: True, if cfgupgrade --downgrade should be called.
"""
return version[0] == current[0] and version[1] == current[1] - 1
def IsCorrectConfigVersion(targetversion, configversion):
"""Decide whether configuration version is compatible with the target.
@param targetversion: The version to upgrade to as (major, minor, revision)
@type targetversion: tuple
@param configversion: The version of the current configuration
@type configversion: tuple
@rtype: bool
@return: True, if the configversion fits with the target version.
"""
return (configversion[0] == targetversion[0] and
configversion[1] == targetversion[1])
def IsBefore(version, major, minor, revision):
"""Decide if a given version is strictly before a given version.
@param version: (major, minor, revision) or None, with None being
before all versions
@type version: (int, int, int) or None
@param major: major version
@type major: int
@param minor: minor version
@type minor: int
@param revision: revision
@type revision: int
"""
if version is None:
return True
return version < (major, minor, revision)
def IsEqual(version, major, minor, revision):
"""Decide if a given version matches the given version.
If the revision is set to None, only major and minor are compared.
@param version: (major, minor, revision) or None, with None being
before all versions
@type version: (int, int, int) or None
@param major: major version
@type major: int
@param minor: minor version
@type minor: int
@param revision: revision
@type revision: int
"""
if version is None:
return False
if revision is None:
current_major, current_minor, _ = version
return (current_major, current_minor) == (major, minor)
return version == (major, minor, revision)