Add predicate whether to call cfgupgrade --downgrade
[ganeti-local] / lib / utils / version.py
1 #
2 #
3
4 # Copyright (C) 2013 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Version utilities."""
23
24 import re
25
26 from ganeti import constants
27
28 _FULL_VERSION_RE = re.compile(r"(\d+)\.(\d+)\.(\d+)")
29 _SHORT_VERSION_RE = re.compile(r"(\d+)\.(\d+)")
30
31 # The first Ganeti version that supports automatic upgrades
32 FIRST_UPGRADE_VERSION = (2, 10, 0)
33
34 CURRENT_VERSION = (constants.VERSION_MAJOR, constants.VERSION_MINOR,
35                    constants.VERSION_REVISION)
36
37 # Format for CONFIG_VERSION:
38 #   01 03 0123 = 01030123
39 #   ^^ ^^ ^^^^
40 #   |  |  + Configuration version/revision
41 #   |  + Minor version
42 #   + Major version
43 #
44 # It is stored as an integer. Make sure not to write an octal number.
45
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
49 # here.
50
51
52 def BuildVersion(major, minor, revision):
53   """Calculates int version number from major, minor and revision numbers.
54
55   Returns: int representing version number
56
57   """
58   assert isinstance(major, int)
59   assert isinstance(minor, int)
60   assert isinstance(revision, int)
61   return (1000000 * major +
62             10000 * minor +
63                 1 * revision)
64
65
66 def SplitVersion(version):
67   """Splits version number stored in an int.
68
69   Returns: tuple; (major, minor, revision)
70
71   """
72   assert isinstance(version, int)
73
74   (major, remainder) = divmod(version, 1000000)
75   (minor, revision) = divmod(remainder, 10000)
76
77   return (major, minor, revision)
78
79
80 def ParseVersion(versionstring):
81   """Parses a version string.
82
83   @param versionstring: the version string to parse
84   @type versionstring: string
85   @rtype: tuple or None
86   @return: (major, minor, revision) if parsable, None otherwise.
87
88   """
89   m = _FULL_VERSION_RE.match(versionstring)
90   if m is not None:
91     return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
92
93   m = _SHORT_VERSION_RE.match(versionstring)
94   if m is not None:
95     return (int(m.group(1)), int(m.group(2)), 0)
96
97   return None
98
99
100 def UpgradeRange(target, current=CURRENT_VERSION):
101   """Verify whether a version is within the range of automatic upgrades.
102
103   @param target: The version to upgrade to as (major, minor, revision)
104   @type target: tuple
105   @param current: The version to upgrade from as (major, minor, revision)
106   @type current: tuple
107   @rtype: string or None
108   @return: None, if within the range, and a human-readable error message
109       otherwise
110
111   """
112   if target < FIRST_UPGRADE_VERSION or current < FIRST_UPGRADE_VERSION:
113     return "automatic upgrades only supported from 2.10 onwards"
114
115   if target[0] != current[0]:
116     return "different major versions"
117
118   if target[1] < current[1] - 1:
119     return "can only downgrade one minor version at a time"
120
121   return None
122
123
124 def ShouldCfgdowngrade(version, current=CURRENT_VERSION):
125   """Decide whether cfgupgrade --downgrade should be called.
126
127   Given the current version and the version to change to, decide
128   if in the transition process cfgupgrade --downgrade should
129   be called
130
131   @param version: The version to upgrade to as (major, minor, revision)
132   @type version: tuple
133   @param current: The versino to upgrade from as (major, minor, revision)
134   @type current: tuple
135   @rtype: bool
136   @return: True, if cfgupgrade --downgrade should be called.
137
138   """
139   return version[0] == current[0] and version[1] == current[1] - 1