4 # Copyright (C) 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Version utilities."""
26 from ganeti import constants
28 _FULL_VERSION_RE = re.compile(r"(\d+)\.(\d+)\.(\d+)")
29 _SHORT_VERSION_RE = re.compile(r"(\d+)\.(\d+)")
31 # The first Ganeti version that supports automatic upgrades
32 FIRST_UPGRADE_VERSION = (2, 10, 0)
34 CURRENT_VERSION = (constants.VERSION_MAJOR, constants.VERSION_MINOR,
35 constants.VERSION_REVISION)
37 # Format for CONFIG_VERSION:
38 # 01 03 0123 = 01030123
40 # | | + Configuration version/revision
44 # It is stored as an integer. Make sure not to write an octal number.
46 # BuildVersion and SplitVersion must be in here because we can't import other
47 # modules. The cfgupgrade tool must be able to read and write version numbers
48 # and thus requires these functions. To avoid code duplication, they're kept in
52 def BuildVersion(major, minor, revision):
53 """Calculates int version number from major, minor and revision numbers.
55 Returns: int representing version number
58 assert isinstance(major, int)
59 assert isinstance(minor, int)
60 assert isinstance(revision, int)
61 return (1000000 * major +
66 def SplitVersion(version):
67 """Splits version number stored in an int.
69 Returns: tuple; (major, minor, revision)
72 assert isinstance(version, int)
74 (major, remainder) = divmod(version, 1000000)
75 (minor, revision) = divmod(remainder, 10000)
77 return (major, minor, revision)
80 def ParseVersion(versionstring):
81 """Parses a version string.
83 @param versionstring: the version string to parse
84 @type versionstring: string
86 @return: (major, minor, revision) if parsable, None otherwise.
89 m = _FULL_VERSION_RE.match(versionstring)
91 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
93 m = _SHORT_VERSION_RE.match(versionstring)
95 return (int(m.group(1)), int(m.group(2)), 0)
100 def UpgradeRange(target, current=CURRENT_VERSION):
101 """Verify whether a version is within the range of automatic upgrades.
103 @param target: The version to upgrade to as (major, minor, revision)
105 @param current: The version to upgrade from as (major, minor, revision)
107 @rtype: string or None
108 @return: None, if within the range, and a human-readable error message
112 if target < FIRST_UPGRADE_VERSION or current < FIRST_UPGRADE_VERSION:
113 return "automatic upgrades only supported from 2.10 onwards"
115 if target[0] != current[0]:
116 return "different major versions"
118 if target[1] < current[1] - 1:
119 return "can only downgrade one minor version at a time"
124 def ShouldCfgdowngrade(version, current=CURRENT_VERSION):
125 """Decide whether cfgupgrade --downgrade should be called.
127 Given the current version and the version to change to, decide
128 if in the transition process cfgupgrade --downgrade should
131 @param version: The version to upgrade to as (major, minor, revision)
133 @param current: The versino to upgrade from as (major, minor, revision)
136 @return: True, if cfgupgrade --downgrade should be called.
139 return version[0] == current[0] and version[1] == current[1] - 1