Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 1d9f9df7

History | View | Annotate | Download (22 kB)

1 0006af7d Michael Hanselmann
#!/usr/bin/python
2 0006af7d Michael Hanselmann
#
3 0006af7d Michael Hanselmann
4 fdb85e3d Bernardo Dal Seno
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 0006af7d Michael Hanselmann
#
6 0006af7d Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 0006af7d Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 0006af7d Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 0006af7d Michael Hanselmann
# (at your option) any later version.
10 0006af7d Michael Hanselmann
#
11 0006af7d Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 0006af7d Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 0006af7d Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 0006af7d Michael Hanselmann
# General Public License for more details.
15 0006af7d Michael Hanselmann
#
16 0006af7d Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 0006af7d Michael Hanselmann
# along with this program; if not, write to the Free Software
18 0006af7d Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 0006af7d Michael Hanselmann
# 02110-1301, USA.
20 0006af7d Michael Hanselmann
21 0006af7d Michael Hanselmann
22 0006af7d Michael Hanselmann
"""Tool to upgrade the configuration file.
23 0006af7d Michael Hanselmann
24 a421fdeb Iustin Pop
This code handles only the types supported by simplejson. As an
25 a421fdeb Iustin Pop
example, 'set' is a 'list'.
26 0006af7d Michael Hanselmann
27 0006af7d Michael Hanselmann
"""
28 0006af7d Michael Hanselmann
29 0006af7d Michael Hanselmann
30 0006af7d Michael Hanselmann
import os
31 0006af7d Michael Hanselmann
import os.path
32 0006af7d Michael Hanselmann
import sys
33 0006af7d Michael Hanselmann
import optparse
34 eda37a5a Michael Hanselmann
import logging
35 7939f60c Michael Hanselmann
import time
36 7939f60c Michael Hanselmann
from cStringIO import StringIO
37 0006af7d Michael Hanselmann
38 95e4a814 Michael Hanselmann
from ganeti import constants
39 95e4a814 Michael Hanselmann
from ganeti import serializer
40 319856a9 Michael Hanselmann
from ganeti import utils
41 f97c7901 Michael Hanselmann
from ganeti import cli
42 a421fdeb Iustin Pop
from ganeti import bootstrap
43 ac4d25b6 Iustin Pop
from ganeti import config
44 011974df Michael Hanselmann
from ganeti import netutils
45 09bf5d24 Michael Hanselmann
from ganeti import pathutils
46 0006af7d Michael Hanselmann
47 0006af7d Michael Hanselmann
48 319856a9 Michael Hanselmann
options = None
49 319856a9 Michael Hanselmann
args = None
50 0006af7d Michael Hanselmann
51 6f285030 Iustin Pop
52 93fd9bb1 Iustin Pop
#: Target major version we will upgrade to
53 93fd9bb1 Iustin Pop
TARGET_MAJOR = 2
54 93fd9bb1 Iustin Pop
#: Target minor version we will upgrade to
55 ea2ee4b0 Michele Tartara
TARGET_MINOR = 9
56 1709435e Bernardo Dal Seno
#: Target major version for downgrade
57 1709435e Bernardo Dal Seno
DOWNGRADE_MAJOR = 2
58 1709435e Bernardo Dal Seno
#: Target minor version for downgrade
59 ea2ee4b0 Michele Tartara
DOWNGRADE_MINOR = 8
60 93fd9bb1 Iustin Pop
61 7187a877 Helga Velroyen
# map of legacy device types
62 7187a877 Helga Velroyen
# (mapping differing old LD_* constants to new DT_* constants)
63 7187a877 Helga Velroyen
DEV_TYPE_OLD_NEW = {"lvm": constants.DT_PLAIN, "drbd8": constants.DT_DRBD8}
64 7187a877 Helga Velroyen
# (mapping differing new DT_* constants to old LD_* constants)
65 8cb2b4f4 Helga Velroyen
DEV_TYPE_NEW_OLD = dict((v, k) for k, v in DEV_TYPE_OLD_NEW.items())
66 7187a877 Helga Velroyen
67 93fd9bb1 Iustin Pop
68 319856a9 Michael Hanselmann
class Error(Exception):
69 319856a9 Michael Hanselmann
  """Generic exception"""
70 319856a9 Michael Hanselmann
  pass
71 0006af7d Michael Hanselmann
72 0006af7d Michael Hanselmann
73 eda37a5a Michael Hanselmann
def SetupLogging():
74 eda37a5a Michael Hanselmann
  """Configures the logging module.
75 eda37a5a Michael Hanselmann
76 eda37a5a Michael Hanselmann
  """
77 eda37a5a Michael Hanselmann
  formatter = logging.Formatter("%(asctime)s: %(message)s")
78 eda37a5a Michael Hanselmann
79 eda37a5a Michael Hanselmann
  stderr_handler = logging.StreamHandler()
80 eda37a5a Michael Hanselmann
  stderr_handler.setFormatter(formatter)
81 eda37a5a Michael Hanselmann
  if options.debug:
82 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.NOTSET)
83 eda37a5a Michael Hanselmann
  elif options.verbose:
84 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.INFO)
85 eda37a5a Michael Hanselmann
  else:
86 011974df Michael Hanselmann
    stderr_handler.setLevel(logging.WARNING)
87 eda37a5a Michael Hanselmann
88 eda37a5a Michael Hanselmann
  root_logger = logging.getLogger("")
89 eda37a5a Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
90 eda37a5a Michael Hanselmann
  root_logger.addHandler(stderr_handler)
91 eda37a5a Michael Hanselmann
92 eda37a5a Michael Hanselmann
93 011974df Michael Hanselmann
def CheckHostname(path):
94 011974df Michael Hanselmann
  """Ensures hostname matches ssconf value.
95 011974df Michael Hanselmann
96 011974df Michael Hanselmann
  @param path: Path to ssconf file
97 011974df Michael Hanselmann
98 011974df Michael Hanselmann
  """
99 011974df Michael Hanselmann
  ssconf_master_node = utils.ReadOneLineFile(path)
100 011974df Michael Hanselmann
  hostname = netutils.GetHostname().name
101 011974df Michael Hanselmann
102 011974df Michael Hanselmann
  if ssconf_master_node == hostname:
103 011974df Michael Hanselmann
    return True
104 011974df Michael Hanselmann
105 011974df Michael Hanselmann
  logging.warning("Warning: ssconf says master node is '%s', but this"
106 011974df Michael Hanselmann
                  " machine's name is '%s'; this tool must be run on"
107 011974df Michael Hanselmann
                  " the master node", ssconf_master_node, hostname)
108 011974df Michael Hanselmann
  return False
109 011974df Michael Hanselmann
110 3c286190 Dimitris Aragiorgis
111 e94fc80c Bernardo Dal Seno
def _FillIPolicySpecs(default_ipolicy, ipolicy):
112 e94fc80c Bernardo Dal Seno
  if "minmax" in ipolicy:
113 41044e04 Bernardo Dal Seno
    for (key, spec) in ipolicy["minmax"][0].items():
114 41044e04 Bernardo Dal Seno
      for (par, val) in default_ipolicy["minmax"][0][key].items():
115 e94fc80c Bernardo Dal Seno
        if par not in spec:
116 e94fc80c Bernardo Dal Seno
          spec[par] = val
117 e94fc80c Bernardo Dal Seno
118 e94fc80c Bernardo Dal Seno
119 e94fc80c Bernardo Dal Seno
def UpgradeIPolicy(ipolicy, default_ipolicy, isgroup):
120 0b94cda8 Bernardo Dal Seno
  minmax_keys = ["min", "max"]
121 0b94cda8 Bernardo Dal Seno
  if any((k in ipolicy) for k in minmax_keys):
122 0b94cda8 Bernardo Dal Seno
    minmax = {}
123 0b94cda8 Bernardo Dal Seno
    for key in minmax_keys:
124 0b94cda8 Bernardo Dal Seno
      if key in ipolicy:
125 e94fc80c Bernardo Dal Seno
        if ipolicy[key]:
126 e94fc80c Bernardo Dal Seno
          minmax[key] = ipolicy[key]
127 0b94cda8 Bernardo Dal Seno
        del ipolicy[key]
128 e94fc80c Bernardo Dal Seno
    if minmax:
129 41044e04 Bernardo Dal Seno
      ipolicy["minmax"] = [minmax]
130 e94fc80c Bernardo Dal Seno
  if isgroup and "std" in ipolicy:
131 e94fc80c Bernardo Dal Seno
    del ipolicy["std"]
132 e94fc80c Bernardo Dal Seno
  _FillIPolicySpecs(default_ipolicy, ipolicy)
133 0b94cda8 Bernardo Dal Seno
134 0b94cda8 Bernardo Dal Seno
135 58bf877f Dimitris Aragiorgis
def UpgradeNetworks(config_data):
136 58bf877f Dimitris Aragiorgis
  networks = config_data.get("networks", None)
137 58bf877f Dimitris Aragiorgis
  if not networks:
138 58bf877f Dimitris Aragiorgis
    config_data["networks"] = {}
139 58bf877f Dimitris Aragiorgis
140 58bf877f Dimitris Aragiorgis
141 0b94cda8 Bernardo Dal Seno
def UpgradeCluster(config_data):
142 0b94cda8 Bernardo Dal Seno
  cluster = config_data.get("cluster", None)
143 0b94cda8 Bernardo Dal Seno
  if cluster is None:
144 0b94cda8 Bernardo Dal Seno
    raise Error("Cannot find cluster")
145 e94fc80c Bernardo Dal Seno
  ipolicy = cluster.setdefault("ipolicy", None)
146 0b94cda8 Bernardo Dal Seno
  if ipolicy:
147 e94fc80c Bernardo Dal Seno
    UpgradeIPolicy(ipolicy, constants.IPOLICY_DEFAULTS, False)
148 0b94cda8 Bernardo Dal Seno
149 0b94cda8 Bernardo Dal Seno
150 58bf877f Dimitris Aragiorgis
def UpgradeGroups(config_data):
151 e94fc80c Bernardo Dal Seno
  cl_ipolicy = config_data["cluster"].get("ipolicy")
152 58bf877f Dimitris Aragiorgis
  for group in config_data["nodegroups"].values():
153 58bf877f Dimitris Aragiorgis
    networks = group.get("networks", None)
154 58bf877f Dimitris Aragiorgis
    if not networks:
155 58bf877f Dimitris Aragiorgis
      group["networks"] = {}
156 0b94cda8 Bernardo Dal Seno
    ipolicy = group.get("ipolicy", None)
157 0b94cda8 Bernardo Dal Seno
    if ipolicy:
158 e94fc80c Bernardo Dal Seno
      if cl_ipolicy is None:
159 e94fc80c Bernardo Dal Seno
        raise Error("A group defines an instance policy but there is no"
160 e94fc80c Bernardo Dal Seno
                    " instance policy at cluster level")
161 e94fc80c Bernardo Dal Seno
      UpgradeIPolicy(ipolicy, cl_ipolicy, True)
162 58bf877f Dimitris Aragiorgis
163 011974df Michael Hanselmann
164 c69b147d Bernardo Dal Seno
def GetExclusiveStorageValue(config_data):
165 c69b147d Bernardo Dal Seno
  """Return a conservative value of the exclusive_storage flag.
166 c69b147d Bernardo Dal Seno
167 c69b147d Bernardo Dal Seno
  Return C{True} if the cluster or at least a nodegroup have the flag set.
168 c69b147d Bernardo Dal Seno
169 c69b147d Bernardo Dal Seno
  """
170 c69b147d Bernardo Dal Seno
  ret = False
171 c69b147d Bernardo Dal Seno
  cluster = config_data["cluster"]
172 c69b147d Bernardo Dal Seno
  ndparams = cluster.get("ndparams")
173 c69b147d Bernardo Dal Seno
  if ndparams is not None and ndparams.get("exclusive_storage"):
174 c69b147d Bernardo Dal Seno
    ret = True
175 c69b147d Bernardo Dal Seno
  for group in config_data["nodegroups"].values():
176 c69b147d Bernardo Dal Seno
    ndparams = group.get("ndparams")
177 c69b147d Bernardo Dal Seno
    if ndparams is not None and ndparams.get("exclusive_storage"):
178 c69b147d Bernardo Dal Seno
      ret = True
179 c69b147d Bernardo Dal Seno
  return ret
180 c69b147d Bernardo Dal Seno
181 c69b147d Bernardo Dal Seno
182 7187a877 Helga Velroyen
def ChangeDiskDevType(disk, dev_type_map):
183 7187a877 Helga Velroyen
  """Replaces disk's dev_type attributes according to the given map.
184 7187a877 Helga Velroyen
185 7187a877 Helga Velroyen
  This can be used for both, up or downgrading the disks.
186 7187a877 Helga Velroyen
  """
187 7187a877 Helga Velroyen
  if disk["dev_type"] in dev_type_map:
188 7187a877 Helga Velroyen
    disk["dev_type"] = dev_type_map[disk["dev_type"]]
189 7187a877 Helga Velroyen
  if "children" in disk:
190 7187a877 Helga Velroyen
    for child in disk["children"]:
191 7187a877 Helga Velroyen
      ChangeDiskDevType(child, dev_type_map)
192 7187a877 Helga Velroyen
193 7187a877 Helga Velroyen
194 7187a877 Helga Velroyen
def UpgradeDiskDevType(disk):
195 7187a877 Helga Velroyen
  """Upgrades the disks' device type."""
196 7187a877 Helga Velroyen
  ChangeDiskDevType(disk, DEV_TYPE_OLD_NEW)
197 7187a877 Helga Velroyen
198 7187a877 Helga Velroyen
199 f032d55c Dimitris Aragiorgis
def UpgradeInstances(config_data):
200 7187a877 Helga Velroyen
  """Upgrades the instances' configuration."""
201 7187a877 Helga Velroyen
202 f032d55c Dimitris Aragiorgis
  network2uuid = dict((n["name"], n["uuid"])
203 f032d55c Dimitris Aragiorgis
                      for n in config_data["networks"].values())
204 bb553e5a Bernardo Dal Seno
  if "instances" not in config_data:
205 bb553e5a Bernardo Dal Seno
    raise Error("Can't find the 'instances' key in the configuration!")
206 bb553e5a Bernardo Dal Seno
207 c69b147d Bernardo Dal Seno
  missing_spindles = False
208 bb553e5a Bernardo Dal Seno
  for instance, iobj in config_data["instances"].items():
209 bb553e5a Bernardo Dal Seno
    for nic in iobj["nics"]:
210 f032d55c Dimitris Aragiorgis
      name = nic.get("network", None)
211 f032d55c Dimitris Aragiorgis
      if name:
212 f032d55c Dimitris Aragiorgis
        uuid = network2uuid.get(name, None)
213 f032d55c Dimitris Aragiorgis
        if uuid:
214 f032d55c Dimitris Aragiorgis
          print("NIC with network name %s found."
215 f032d55c Dimitris Aragiorgis
                " Substituting with uuid %s." % (name, uuid))
216 f032d55c Dimitris Aragiorgis
          nic["network"] = uuid
217 f032d55c Dimitris Aragiorgis
218 bb553e5a Bernardo Dal Seno
    if "disks" not in iobj:
219 bb553e5a Bernardo Dal Seno
      raise Error("Instance '%s' doesn't have a disks entry?!" % instance)
220 bb553e5a Bernardo Dal Seno
    disks = iobj["disks"]
221 bb553e5a Bernardo Dal Seno
    for idx, dobj in enumerate(disks):
222 bb553e5a Bernardo Dal Seno
      expected = "disk/%s" % idx
223 bb553e5a Bernardo Dal Seno
      current = dobj.get("iv_name", "")
224 bb553e5a Bernardo Dal Seno
      if current != expected:
225 bb553e5a Bernardo Dal Seno
        logging.warning("Updating iv_name for instance %s/disk %s"
226 bb553e5a Bernardo Dal Seno
                        " from '%s' to '%s'",
227 bb553e5a Bernardo Dal Seno
                        instance, idx, current, expected)
228 bb553e5a Bernardo Dal Seno
        dobj["iv_name"] = expected
229 7187a877 Helga Velroyen
230 7187a877 Helga Velroyen
      if "dev_type" in dobj:
231 7187a877 Helga Velroyen
        UpgradeDiskDevType(dobj)
232 7187a877 Helga Velroyen
233 c69b147d Bernardo Dal Seno
      if not "spindles" in dobj:
234 c69b147d Bernardo Dal Seno
        missing_spindles = True
235 c69b147d Bernardo Dal Seno
236 c69b147d Bernardo Dal Seno
  if GetExclusiveStorageValue(config_data) and missing_spindles:
237 c69b147d Bernardo Dal Seno
    # We cannot be sure that the instances that are missing spindles have
238 c69b147d Bernardo Dal Seno
    # exclusive storage enabled (the check would be more complicated), so we
239 c69b147d Bernardo Dal Seno
    # give a noncommittal message
240 c69b147d Bernardo Dal Seno
    logging.warning("Some instance disks could be needing to update the"
241 c69b147d Bernardo Dal Seno
                    " spindles parameter; you can check by running"
242 c69b147d Bernardo Dal Seno
                    " 'gnt-cluster verify', and fix any problem with"
243 c69b147d Bernardo Dal Seno
                    " 'gnt-cluster repair-disk-sizes'")
244 bb553e5a Bernardo Dal Seno
245 bb553e5a Bernardo Dal Seno
246 bb553e5a Bernardo Dal Seno
def UpgradeRapiUsers():
247 bb553e5a Bernardo Dal Seno
  if (os.path.isfile(options.RAPI_USERS_FILE_PRE24) and
248 bb553e5a Bernardo Dal Seno
      not os.path.islink(options.RAPI_USERS_FILE_PRE24)):
249 bb553e5a Bernardo Dal Seno
    if os.path.exists(options.RAPI_USERS_FILE):
250 bb553e5a Bernardo Dal Seno
      raise Error("Found pre-2.4 RAPI users file at %s, but another file"
251 bb553e5a Bernardo Dal Seno
                  " already exists at %s" %
252 bb553e5a Bernardo Dal Seno
                  (options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE))
253 bb553e5a Bernardo Dal Seno
    logging.info("Found pre-2.4 RAPI users file at %s, renaming to %s",
254 bb553e5a Bernardo Dal Seno
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
255 bb553e5a Bernardo Dal Seno
    if not options.dry_run:
256 bb553e5a Bernardo Dal Seno
      utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
257 bb553e5a Bernardo Dal Seno
                       mkdir=True, mkdir_mode=0750)
258 bb553e5a Bernardo Dal Seno
259 bb553e5a Bernardo Dal Seno
  # Create a symlink for RAPI users file
260 bb553e5a Bernardo Dal Seno
  if (not (os.path.islink(options.RAPI_USERS_FILE_PRE24) or
261 bb553e5a Bernardo Dal Seno
           os.path.isfile(options.RAPI_USERS_FILE_PRE24)) and
262 bb553e5a Bernardo Dal Seno
      os.path.isfile(options.RAPI_USERS_FILE)):
263 bb553e5a Bernardo Dal Seno
    logging.info("Creating symlink from %s to %s",
264 bb553e5a Bernardo Dal Seno
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
265 bb553e5a Bernardo Dal Seno
    if not options.dry_run:
266 bb553e5a Bernardo Dal Seno
      os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
267 bb553e5a Bernardo Dal Seno
268 bb553e5a Bernardo Dal Seno
269 bb553e5a Bernardo Dal Seno
def UpgradeWatcher():
270 bb553e5a Bernardo Dal Seno
  # Remove old watcher state file if it exists
271 bb553e5a Bernardo Dal Seno
  if os.path.exists(options.WATCHER_STATEFILE):
272 bb553e5a Bernardo Dal Seno
    logging.info("Removing watcher state file %s", options.WATCHER_STATEFILE)
273 bb553e5a Bernardo Dal Seno
    if not options.dry_run:
274 bb553e5a Bernardo Dal Seno
      utils.RemoveFile(options.WATCHER_STATEFILE)
275 bb553e5a Bernardo Dal Seno
276 bb553e5a Bernardo Dal Seno
277 bb553e5a Bernardo Dal Seno
def UpgradeFileStoragePaths(config_data):
278 bb553e5a Bernardo Dal Seno
  # Write file storage paths
279 bb553e5a Bernardo Dal Seno
  if not os.path.exists(options.FILE_STORAGE_PATHS_FILE):
280 bb553e5a Bernardo Dal Seno
    cluster = config_data["cluster"]
281 bb553e5a Bernardo Dal Seno
    file_storage_dir = cluster.get("file_storage_dir")
282 bb553e5a Bernardo Dal Seno
    shared_file_storage_dir = cluster.get("shared_file_storage_dir")
283 bb553e5a Bernardo Dal Seno
    del cluster
284 bb553e5a Bernardo Dal Seno
285 bb553e5a Bernardo Dal Seno
    logging.info("Ganeti 2.7 and later only allow whitelisted directories"
286 bb553e5a Bernardo Dal Seno
                 " for file storage; writing existing configuration values"
287 bb553e5a Bernardo Dal Seno
                 " into '%s'",
288 bb553e5a Bernardo Dal Seno
                 options.FILE_STORAGE_PATHS_FILE)
289 bb553e5a Bernardo Dal Seno
290 bb553e5a Bernardo Dal Seno
    if file_storage_dir:
291 bb553e5a Bernardo Dal Seno
      logging.info("File storage directory: %s", file_storage_dir)
292 bb553e5a Bernardo Dal Seno
    if shared_file_storage_dir:
293 bb553e5a Bernardo Dal Seno
      logging.info("Shared file storage directory: %s",
294 bb553e5a Bernardo Dal Seno
                   shared_file_storage_dir)
295 bb553e5a Bernardo Dal Seno
296 bb553e5a Bernardo Dal Seno
    buf = StringIO()
297 bb553e5a Bernardo Dal Seno
    buf.write("# List automatically generated from configuration by\n")
298 bb553e5a Bernardo Dal Seno
    buf.write("# cfgupgrade at %s\n" % time.asctime())
299 bb553e5a Bernardo Dal Seno
    if file_storage_dir:
300 bb553e5a Bernardo Dal Seno
      buf.write("%s\n" % file_storage_dir)
301 bb553e5a Bernardo Dal Seno
    if shared_file_storage_dir:
302 bb553e5a Bernardo Dal Seno
      buf.write("%s\n" % shared_file_storage_dir)
303 bb553e5a Bernardo Dal Seno
    utils.WriteFile(file_name=options.FILE_STORAGE_PATHS_FILE,
304 bb553e5a Bernardo Dal Seno
                    data=buf.getvalue(),
305 bb553e5a Bernardo Dal Seno
                    mode=0600,
306 bb553e5a Bernardo Dal Seno
                    dry_run=options.dry_run,
307 bb553e5a Bernardo Dal Seno
                    backup=True)
308 bb553e5a Bernardo Dal Seno
309 bb553e5a Bernardo Dal Seno
310 b555101c Thomas Thrainer
def GetNewNodeIndex(nodes_by_old_key, old_key, new_key_field):
311 b555101c Thomas Thrainer
  if old_key not in nodes_by_old_key:
312 b555101c Thomas Thrainer
    logging.warning("Can't find node '%s' in configuration, assuming that it's"
313 b555101c Thomas Thrainer
                    " already up-to-date", old_key)
314 b555101c Thomas Thrainer
    return old_key
315 b555101c Thomas Thrainer
  return nodes_by_old_key[old_key][new_key_field]
316 b555101c Thomas Thrainer
317 b555101c Thomas Thrainer
318 b555101c Thomas Thrainer
def ChangeNodeIndices(config_data, old_key_field, new_key_field):
319 b555101c Thomas Thrainer
  def ChangeDiskNodeIndices(disk):
320 7187a877 Helga Velroyen
    # Note: 'drbd8' is a legacy device type from pre 2.9 and needs to be
321 7187a877 Helga Velroyen
    # considered when up/downgrading from/to any versions touching 2.9 on the
322 7187a877 Helga Velroyen
    # way.
323 7187a877 Helga Velroyen
    drbd_disk_types = set(["drbd8"]) | constants.DTS_DRBD
324 7187a877 Helga Velroyen
    if disk["dev_type"] in drbd_disk_types:
325 b555101c Thomas Thrainer
      for i in range(0, 2):
326 b555101c Thomas Thrainer
        disk["logical_id"][i] = GetNewNodeIndex(nodes_by_old_key,
327 b555101c Thomas Thrainer
                                                disk["logical_id"][i],
328 b555101c Thomas Thrainer
                                                new_key_field)
329 b555101c Thomas Thrainer
    if "children" in disk:
330 b555101c Thomas Thrainer
      for child in disk["children"]:
331 b555101c Thomas Thrainer
        ChangeDiskNodeIndices(child)
332 b555101c Thomas Thrainer
333 b555101c Thomas Thrainer
  nodes_by_old_key = {}
334 b555101c Thomas Thrainer
  nodes_by_new_key = {}
335 b555101c Thomas Thrainer
  for (_, node) in config_data["nodes"].items():
336 b555101c Thomas Thrainer
    nodes_by_old_key[node[old_key_field]] = node
337 b555101c Thomas Thrainer
    nodes_by_new_key[node[new_key_field]] = node
338 b555101c Thomas Thrainer
339 b555101c Thomas Thrainer
  config_data["nodes"] = nodes_by_new_key
340 b555101c Thomas Thrainer
341 b555101c Thomas Thrainer
  cluster = config_data["cluster"]
342 b555101c Thomas Thrainer
  cluster["master_node"] = GetNewNodeIndex(nodes_by_old_key,
343 b555101c Thomas Thrainer
                                           cluster["master_node"],
344 b555101c Thomas Thrainer
                                           new_key_field)
345 b555101c Thomas Thrainer
346 b555101c Thomas Thrainer
  for inst in config_data["instances"].values():
347 b555101c Thomas Thrainer
    inst["primary_node"] = GetNewNodeIndex(nodes_by_old_key,
348 b555101c Thomas Thrainer
                                           inst["primary_node"],
349 b555101c Thomas Thrainer
                                           new_key_field)
350 b555101c Thomas Thrainer
    for disk in inst["disks"]:
351 b555101c Thomas Thrainer
      ChangeDiskNodeIndices(disk)
352 b555101c Thomas Thrainer
353 b555101c Thomas Thrainer
354 4d33e134 Thomas Thrainer
def ChangeInstanceIndices(config_data, old_key_field, new_key_field):
355 4d33e134 Thomas Thrainer
  insts_by_old_key = {}
356 4d33e134 Thomas Thrainer
  insts_by_new_key = {}
357 4d33e134 Thomas Thrainer
  for (_, inst) in config_data["instances"].items():
358 4d33e134 Thomas Thrainer
    insts_by_old_key[inst[old_key_field]] = inst
359 4d33e134 Thomas Thrainer
    insts_by_new_key[inst[new_key_field]] = inst
360 4d33e134 Thomas Thrainer
361 4d33e134 Thomas Thrainer
  config_data["instances"] = insts_by_new_key
362 4d33e134 Thomas Thrainer
363 4d33e134 Thomas Thrainer
364 b555101c Thomas Thrainer
def UpgradeNodeIndices(config_data):
365 b555101c Thomas Thrainer
  ChangeNodeIndices(config_data, "name", "uuid")
366 b555101c Thomas Thrainer
367 b555101c Thomas Thrainer
368 4d33e134 Thomas Thrainer
def UpgradeInstanceIndices(config_data):
369 4d33e134 Thomas Thrainer
  ChangeInstanceIndices(config_data, "name", "uuid")
370 4d33e134 Thomas Thrainer
371 4d33e134 Thomas Thrainer
372 bb553e5a Bernardo Dal Seno
def UpgradeAll(config_data):
373 bb553e5a Bernardo Dal Seno
  config_data["version"] = constants.BuildVersion(TARGET_MAJOR,
374 bb553e5a Bernardo Dal Seno
                                                  TARGET_MINOR, 0)
375 bb553e5a Bernardo Dal Seno
  UpgradeRapiUsers()
376 bb553e5a Bernardo Dal Seno
  UpgradeWatcher()
377 bb553e5a Bernardo Dal Seno
  UpgradeFileStoragePaths(config_data)
378 bb553e5a Bernardo Dal Seno
  UpgradeNetworks(config_data)
379 0b94cda8 Bernardo Dal Seno
  UpgradeCluster(config_data)
380 bb553e5a Bernardo Dal Seno
  UpgradeGroups(config_data)
381 bb553e5a Bernardo Dal Seno
  UpgradeInstances(config_data)
382 b555101c Thomas Thrainer
  UpgradeNodeIndices(config_data)
383 4d33e134 Thomas Thrainer
  UpgradeInstanceIndices(config_data)
384 bb553e5a Bernardo Dal Seno
385 f032d55c Dimitris Aragiorgis
386 7187a877 Helga Velroyen
def DowngradeDiskDevType(disk):
387 7187a877 Helga Velroyen
  """Downgrades the disks' device type."""
388 7187a877 Helga Velroyen
  ChangeDiskDevType(disk, DEV_TYPE_NEW_OLD)
389 7187a877 Helga Velroyen
390 8cb2b4f4 Helga Velroyen
391 30448dc7 Bernardo Dal Seno
def DowngradeDisks(disks, owner):
392 30448dc7 Bernardo Dal Seno
  for disk in disks:
393 30448dc7 Bernardo Dal Seno
    # Remove spindles to downgrade to 2.8
394 30448dc7 Bernardo Dal Seno
    if "spindles" in disk:
395 30448dc7 Bernardo Dal Seno
      logging.warning("Removing spindles (value=%s) from disk %s (%s) of"
396 30448dc7 Bernardo Dal Seno
                      " instance %s",
397 30448dc7 Bernardo Dal Seno
                      disk["spindles"], disk["iv_name"], disk["uuid"], owner)
398 30448dc7 Bernardo Dal Seno
      del disk["spindles"]
399 7187a877 Helga Velroyen
    if "dev_type" in disk:
400 7187a877 Helga Velroyen
      DowngradeDiskDevType(disk)
401 30448dc7 Bernardo Dal Seno
402 30448dc7 Bernardo Dal Seno
403 30448dc7 Bernardo Dal Seno
def DowngradeInstances(config_data):
404 30448dc7 Bernardo Dal Seno
  if "instances" not in config_data:
405 30448dc7 Bernardo Dal Seno
    raise Error("Cannot find the 'instances' key in the configuration!")
406 30448dc7 Bernardo Dal Seno
  for (iname, iobj) in config_data["instances"].items():
407 30448dc7 Bernardo Dal Seno
    if "disks" not in iobj:
408 30448dc7 Bernardo Dal Seno
      raise Error("Cannot find 'disks' key for instance %s" % iname)
409 30448dc7 Bernardo Dal Seno
    DowngradeDisks(iobj["disks"], iname)
410 30448dc7 Bernardo Dal Seno
411 30448dc7 Bernardo Dal Seno
412 b555101c Thomas Thrainer
def DowngradeNodeIndices(config_data):
413 b555101c Thomas Thrainer
  ChangeNodeIndices(config_data, "uuid", "name")
414 b555101c Thomas Thrainer
415 b555101c Thomas Thrainer
416 4d33e134 Thomas Thrainer
def DowngradeInstanceIndices(config_data):
417 4d33e134 Thomas Thrainer
  ChangeInstanceIndices(config_data, "uuid", "name")
418 4d33e134 Thomas Thrainer
419 4d33e134 Thomas Thrainer
420 1d9f9df7 Helga Velroyen
def DowngradeHvparams(config_data):
421 1d9f9df7 Helga Velroyen
  """Downgrade the cluster's hypervisor parameters."""
422 1d9f9df7 Helga Velroyen
  cluster = config_data["cluster"]
423 1d9f9df7 Helga Velroyen
  if "hvparams" in cluster:
424 1d9f9df7 Helga Velroyen
    hvparams = cluster["hvparams"]
425 1d9f9df7 Helga Velroyen
    xen_params = None
426 1d9f9df7 Helga Velroyen
    for xen_variant in [constants.HT_XEN_PVM, constants.HT_XEN_HVM]:
427 1d9f9df7 Helga Velroyen
      if xen_variant in hvparams:
428 1d9f9df7 Helga Velroyen
        xen_params = hvparams[xen_variant]
429 1d9f9df7 Helga Velroyen
        # 'xen_cmd' was introduced in 2.9
430 1d9f9df7 Helga Velroyen
        if constants.HV_XEN_CMD in xen_params:
431 1d9f9df7 Helga Velroyen
          del xen_params[constants.HV_XEN_CMD]
432 1d9f9df7 Helga Velroyen
433 1d9f9df7 Helga Velroyen
434 1709435e Bernardo Dal Seno
def DowngradeAll(config_data):
435 1709435e Bernardo Dal Seno
  # Any code specific to a particular version should be labeled that way, so
436 1709435e Bernardo Dal Seno
  # it can be removed when updating to the next version.
437 77018a43 Michele Tartara
  config_data["version"] = constants.BuildVersion(DOWNGRADE_MAJOR,
438 77018a43 Michele Tartara
                                                  DOWNGRADE_MINOR, 0)
439 30448dc7 Bernardo Dal Seno
  DowngradeInstances(config_data)
440 b555101c Thomas Thrainer
  DowngradeNodeIndices(config_data)
441 4d33e134 Thomas Thrainer
  DowngradeInstanceIndices(config_data)
442 1d9f9df7 Helga Velroyen
  DowngradeHvparams(config_data)
443 1709435e Bernardo Dal Seno
444 1709435e Bernardo Dal Seno
445 6d691282 Michael Hanselmann
def main():
446 6d691282 Michael Hanselmann
  """Main program.
447 6d691282 Michael Hanselmann
448 6d691282 Michael Hanselmann
  """
449 b459a848 Andrea Spadaccini
  global options, args # pylint: disable=W0603
450 6d691282 Michael Hanselmann
451 0006af7d Michael Hanselmann
  # Option parsing
452 95e4a814 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
453 3ccb3a64 Michael Hanselmann
  parser.add_option("--dry-run", dest="dry_run",
454 60edf71e Michael Hanselmann
                    action="store_true",
455 f4bc1f2c Michael Hanselmann
                    help="Try to do the conversion, but don't write"
456 f4bc1f2c Michael Hanselmann
                         " output file")
457 f97c7901 Michael Hanselmann
  parser.add_option(cli.FORCE_OPT)
458 eda37a5a Michael Hanselmann
  parser.add_option(cli.DEBUG_OPT)
459 9cdb9578 Iustin Pop
  parser.add_option(cli.VERBOSE_OPT)
460 011974df Michael Hanselmann
  parser.add_option("--ignore-hostname", dest="ignore_hostname",
461 011974df Michael Hanselmann
                    action="store_true", default=False,
462 011974df Michael Hanselmann
                    help="Don't abort if hostname doesn't match")
463 3ccb3a64 Michael Hanselmann
  parser.add_option("--path", help="Convert configuration in this"
464 09bf5d24 Michael Hanselmann
                    " directory instead of '%s'" % pathutils.DATA_DIR,
465 09bf5d24 Michael Hanselmann
                    default=pathutils.DATA_DIR, dest="data_dir")
466 7939f60c Michael Hanselmann
  parser.add_option("--confdir",
467 7939f60c Michael Hanselmann
                    help=("Use this directory instead of '%s'" %
468 7939f60c Michael Hanselmann
                          pathutils.CONF_DIR),
469 7939f60c Michael Hanselmann
                    default=pathutils.CONF_DIR, dest="conf_dir")
470 02e1292d Michael Hanselmann
  parser.add_option("--no-verify",
471 02e1292d Michael Hanselmann
                    help="Do not verify configuration after upgrade",
472 02e1292d Michael Hanselmann
                    action="store_true", dest="no_verify", default=False)
473 1709435e Bernardo Dal Seno
  parser.add_option("--downgrade",
474 1709435e Bernardo Dal Seno
                    help="Downgrade to the previous stable version",
475 1709435e Bernardo Dal Seno
                    action="store_true", dest="downgrade", default=False)
476 0006af7d Michael Hanselmann
  (options, args) = parser.parse_args()
477 0006af7d Michael Hanselmann
478 ac4d25b6 Iustin Pop
  # We need to keep filenames locally because they might be renamed between
479 ac4d25b6 Iustin Pop
  # versions.
480 0cddd44d Iustin Pop
  options.data_dir = os.path.abspath(options.data_dir)
481 ac4d25b6 Iustin Pop
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
482 ac4d25b6 Iustin Pop
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
483 ac4d25b6 Iustin Pop
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
484 ac4d25b6 Iustin Pop
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
485 b6267745 Andrea Spadaccini
  options.SPICE_CERT_FILE = options.data_dir + "/spice.pem"
486 b6267745 Andrea Spadaccini
  options.SPICE_CACERT_FILE = options.data_dir + "/spice-ca.pem"
487 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
488 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
489 6b7d5878 Michael Hanselmann
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
490 fc0726b9 Michael Hanselmann
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
491 011974df Michael Hanselmann
  options.SSCONF_MASTER_NODE = options.data_dir + "/ssconf_master_node"
492 a292020f Michael Hanselmann
  options.WATCHER_STATEFILE = options.data_dir + "/watcher.data"
493 7939f60c Michael Hanselmann
  options.FILE_STORAGE_PATHS_FILE = options.conf_dir + "/file-storage-paths"
494 ac4d25b6 Iustin Pop
495 eda37a5a Michael Hanselmann
  SetupLogging()
496 eda37a5a Michael Hanselmann
497 0006af7d Michael Hanselmann
  # Option checking
498 0006af7d Michael Hanselmann
  if args:
499 95e4a814 Michael Hanselmann
    raise Error("No arguments expected")
500 1709435e Bernardo Dal Seno
  if options.downgrade and not options.no_verify:
501 1709435e Bernardo Dal Seno
    options.no_verify = True
502 0006af7d Michael Hanselmann
503 011974df Michael Hanselmann
  # Check master name
504 011974df Michael Hanselmann
  if not (CheckHostname(options.SSCONF_MASTER_NODE) or options.ignore_hostname):
505 011974df Michael Hanselmann
    logging.error("Aborting due to hostname mismatch")
506 011974df Michael Hanselmann
    sys.exit(constants.EXIT_FAILURE)
507 011974df Michael Hanselmann
508 319856a9 Michael Hanselmann
  if not options.force:
509 1709435e Bernardo Dal Seno
    if options.downgrade:
510 1709435e Bernardo Dal Seno
      usertext = ("The configuration is going to be DOWNGRADED to version %s.%s"
511 1709435e Bernardo Dal Seno
                  " Some configuration data might be removed if they don't fit"
512 1709435e Bernardo Dal Seno
                  " in the old format. Please make sure you have read the"
513 1709435e Bernardo Dal Seno
                  " upgrade notes (available in the UPGRADE file and included"
514 1709435e Bernardo Dal Seno
                  " in other documentation formats) to understand what they"
515 1709435e Bernardo Dal Seno
                  " are. Continue with *DOWNGRADING* the configuration?" %
516 1709435e Bernardo Dal Seno
                  (DOWNGRADE_MAJOR, DOWNGRADE_MINOR))
517 1709435e Bernardo Dal Seno
    else:
518 1709435e Bernardo Dal Seno
      usertext = ("Please make sure you have read the upgrade notes for"
519 1709435e Bernardo Dal Seno
                  " Ganeti %s (available in the UPGRADE file and included"
520 1709435e Bernardo Dal Seno
                  " in other documentation formats). Continue with upgrading"
521 1709435e Bernardo Dal Seno
                  " configuration?" % constants.RELEASE_VERSION)
522 f97c7901 Michael Hanselmann
    if not cli.AskUser(usertext):
523 a9221f09 Michael Hanselmann
      sys.exit(constants.EXIT_FAILURE)
524 319856a9 Michael Hanselmann
525 95e4a814 Michael Hanselmann
  # Check whether it's a Ganeti configuration directory
526 ac4d25b6 Iustin Pop
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
527 30acff6c Michael Hanselmann
          os.path.isfile(options.SERVER_PEM_PATH) and
528 ac4d25b6 Iustin Pop
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
529 a9221f09 Michael Hanselmann
    raise Error(("%s does not seem to be a Ganeti configuration"
530 ac4d25b6 Iustin Pop
                 " directory") % options.data_dir)
531 95e4a814 Michael Hanselmann
532 7939f60c Michael Hanselmann
  if not os.path.isdir(options.conf_dir):
533 7939f60c Michael Hanselmann
    raise Error("Not a directory: %s" % options.conf_dir)
534 7939f60c Michael Hanselmann
535 11c31f5c Michael Hanselmann
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
536 a421fdeb Iustin Pop
537 11c31f5c Michael Hanselmann
  try:
538 11c31f5c Michael Hanselmann
    config_version = config_data["version"]
539 11c31f5c Michael Hanselmann
  except KeyError:
540 11c31f5c Michael Hanselmann
    raise Error("Unable to determine configuration version")
541 0006af7d Michael Hanselmann
542 11c31f5c Michael Hanselmann
  (config_major, config_minor, config_revision) = \
543 11c31f5c Michael Hanselmann
    constants.SplitVersion(config_version)
544 319856a9 Michael Hanselmann
545 11c31f5c Michael Hanselmann
  logging.info("Found configuration version %s (%d.%d.%d)",
546 11c31f5c Michael Hanselmann
               config_version, config_major, config_minor, config_revision)
547 319856a9 Michael Hanselmann
548 11c31f5c Michael Hanselmann
  if "config_version" in config_data["cluster"]:
549 11c31f5c Michael Hanselmann
    raise Error("Inconsistent configuration: found config_version in"
550 11c31f5c Michael Hanselmann
                " configuration file")
551 95e4a814 Michael Hanselmann
552 1709435e Bernardo Dal Seno
  # Downgrade to the previous stable version
553 1709435e Bernardo Dal Seno
  if options.downgrade:
554 f2e4363c Michele Tartara
    if not ((config_major == TARGET_MAJOR and config_minor == TARGET_MINOR) or
555 f2e4363c Michele Tartara
            (config_major == DOWNGRADE_MAJOR and
556 f2e4363c Michele Tartara
             config_minor == DOWNGRADE_MINOR)):
557 1709435e Bernardo Dal Seno
      raise Error("Downgrade supported only from the latest version (%s.%s),"
558 1709435e Bernardo Dal Seno
                  " found %s (%s.%s.%s) instead" %
559 1709435e Bernardo Dal Seno
                  (TARGET_MAJOR, TARGET_MINOR, config_version, config_major,
560 1709435e Bernardo Dal Seno
                   config_minor, config_revision))
561 1709435e Bernardo Dal Seno
    DowngradeAll(config_data)
562 1709435e Bernardo Dal Seno
563 ea2ee4b0 Michele Tartara
  # Upgrade from 2.{0..7} to 2.9
564 ea2ee4b0 Michele Tartara
  elif config_major == 2 and config_minor in range(0, 10):
565 aeb0c953 Michael Hanselmann
    if config_revision != 0:
566 a9221f09 Michael Hanselmann
      logging.warning("Config revision is %s, not 0", config_revision)
567 bb553e5a Bernardo Dal Seno
    UpgradeAll(config_data)
568 904910c4 Iustin Pop
569 93fd9bb1 Iustin Pop
  elif config_major == TARGET_MAJOR and config_minor == TARGET_MINOR:
570 a9221f09 Michael Hanselmann
    logging.info("No changes necessary")
571 a9221f09 Michael Hanselmann
572 a9221f09 Michael Hanselmann
  else:
573 a9221f09 Michael Hanselmann
    raise Error("Configuration version %d.%d.%d not supported by this tool" %
574 a9221f09 Michael Hanselmann
                (config_major, config_minor, config_revision))
575 aeb0c953 Michael Hanselmann
576 11c31f5c Michael Hanselmann
  try:
577 11c31f5c Michael Hanselmann
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
578 11c31f5c Michael Hanselmann
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
579 11c31f5c Michael Hanselmann
                    data=serializer.DumpJson(config_data),
580 11c31f5c Michael Hanselmann
                    mode=0600,
581 11c31f5c Michael Hanselmann
                    dry_run=options.dry_run,
582 11c31f5c Michael Hanselmann
                    backup=True)
583 a421fdeb Iustin Pop
584 a421fdeb Iustin Pop
    if not options.dry_run:
585 5ae4945a Iustin Pop
      bootstrap.GenerateClusterCrypto(
586 5ae4945a Iustin Pop
        False, False, False, False, False,
587 5ae4945a Iustin Pop
        nodecert_file=options.SERVER_PEM_PATH,
588 5ae4945a Iustin Pop
        rapicert_file=options.RAPI_CERT_FILE,
589 5ae4945a Iustin Pop
        spicecert_file=options.SPICE_CERT_FILE,
590 5ae4945a Iustin Pop
        spicecacert_file=options.SPICE_CACERT_FILE,
591 5ae4945a Iustin Pop
        hmackey_file=options.CONFD_HMAC_KEY,
592 5ae4945a Iustin Pop
        cds_file=options.CDS_FILE)
593 aeb0c953 Michael Hanselmann
594 a9221f09 Michael Hanselmann
  except Exception:
595 11c31f5c Michael Hanselmann
    logging.critical("Writing configuration failed. It is probably in an"
596 95e4a814 Michael Hanselmann
                     " inconsistent state and needs manual intervention.")
597 95e4a814 Michael Hanselmann
    raise
598 0006af7d Michael Hanselmann
599 ac4d25b6 Iustin Pop
  # test loading the config file
600 fdb85e3d Bernardo Dal Seno
  all_ok = True
601 02e1292d Michael Hanselmann
  if not (options.dry_run or options.no_verify):
602 ac4d25b6 Iustin Pop
    logging.info("Testing the new config file...")
603 ac4d25b6 Iustin Pop
    cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
604 959b6fe5 Apollon Oikonomopoulos
                              accept_foreign=options.ignore_hostname,
605 ac4d25b6 Iustin Pop
                              offline=True)
606 ac4d25b6 Iustin Pop
    # if we reached this, it's all fine
607 ac4d25b6 Iustin Pop
    vrfy = cfg.VerifyConfig()
608 ac4d25b6 Iustin Pop
    if vrfy:
609 ac4d25b6 Iustin Pop
      logging.error("Errors after conversion:")
610 ac4d25b6 Iustin Pop
      for item in vrfy:
611 07b8a2b5 Iustin Pop
        logging.error(" - %s", item)
612 fdb85e3d Bernardo Dal Seno
      all_ok = False
613 fdb85e3d Bernardo Dal Seno
    else:
614 fdb85e3d Bernardo Dal Seno
      logging.info("File loaded successfully after upgrading")
615 ac4d25b6 Iustin Pop
    del cfg
616 ac4d25b6 Iustin Pop
617 1709435e Bernardo Dal Seno
  if options.downgrade:
618 1709435e Bernardo Dal Seno
    action = "downgraded"
619 1709435e Bernardo Dal Seno
    out_ver = "%s.%s" % (DOWNGRADE_MAJOR, DOWNGRADE_MINOR)
620 1709435e Bernardo Dal Seno
  else:
621 1709435e Bernardo Dal Seno
    action = "upgraded"
622 1709435e Bernardo Dal Seno
    out_ver = constants.RELEASE_VERSION
623 fdb85e3d Bernardo Dal Seno
  if all_ok:
624 1709435e Bernardo Dal Seno
    cli.ToStderr("Configuration successfully %s to version %s.",
625 1709435e Bernardo Dal Seno
                 action, out_ver)
626 fdb85e3d Bernardo Dal Seno
  else:
627 1709435e Bernardo Dal Seno
    cli.ToStderr("Configuration %s to version %s, but there are errors."
628 1709435e Bernardo Dal Seno
                 "\nPlease review the file.", action, out_ver)
629 66a66fa7 Michael Hanselmann
630 6d691282 Michael Hanselmann
631 6d691282 Michael Hanselmann
if __name__ == "__main__":
632 6d691282 Michael Hanselmann
  main()