Revision 1709435e

b/test/py/cfgupgrade_unittest.py
1 1
#!/usr/bin/python
2 2
#
3 3

  
4
# Copyright (C) 2010, 2012 Google Inc.
4
# Copyright (C) 2010, 2012, 2013 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
37 37
import testutils
38 38

  
39 39

  
40
def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True):
40
def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True,
41
                downgrade=False):
41 42
  cmd = [sys.executable, "%s/tools/cfgupgrade" % testutils.GetSourceDir(),
42 43
         "--debug", "--force", "--path=%s" % path, "--confdir=%s" % path]
43 44

  
......
47 48
    cmd.append("--dry-run")
48 49
  if no_verify:
49 50
    cmd.append("--no-verify")
51
  if downgrade:
52
    cmd.append("--downgrade")
50 53

  
51 54
  result = utils.RunCmd(cmd, cwd=os.getcwd())
52 55
  if result.failed:
......
347 350
  def testUpgradeCurrent(self):
348 351
    self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
349 352

  
353
  def testDowngrade(self):
354
    self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
355
    oldconf = self._LoadConfig()
356
    _RunUpgrade(self.tmpdir, False, True, downgrade=True)
357
    _RunUpgrade(self.tmpdir, False, True)
358
    newconf = self._LoadConfig()
359
    self.assertEqual(oldconf, newconf)
360

  
361
  def testDowngradeTwice(self):
362
    self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
363
    _RunUpgrade(self.tmpdir, False, True, downgrade=True)
364
    oldconf = self._LoadConfig()
365
    _RunUpgrade(self.tmpdir, False, True, downgrade=True)
366
    newconf = self._LoadConfig()
367
    self.assertEqual(oldconf, newconf)
368

  
350 369
  def testUpgradeDryRunFrom_2_0(self):
351 370
    self._TestSimpleUpgrade(constants.BuildVersion(2, 0, 0), True)
352 371

  
......
371 390
  def testUpgradeCurrentDryRun(self):
372 391
    self._TestSimpleUpgrade(constants.CONFIG_VERSION, True)
373 392

  
393
  def testDowngradeDryRun(self):
394
    self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
395
    oldconf = self._LoadConfig()
396
    _RunUpgrade(self.tmpdir, True, True, downgrade=True)
397
    newconf = self._LoadConfig()
398
    self.assertEqual(oldconf["version"], newconf["version"])
374 399

  
375 400
if __name__ == "__main__":
376 401
  testutils.GanetiTestProgram()
b/tools/cfgupgrade
53 53
TARGET_MAJOR = 2
54 54
#: Target minor version we will upgrade to
55 55
TARGET_MINOR = 7
56
#: Target major version for downgrade
57
DOWNGRADE_MAJOR = 2
58
#: Target minor version for downgrade
59
DOWNGRADE_MINOR = 7
56 60

  
57 61

  
58 62
class Error(Exception):
......
215 219
  UpgradeInstances(config_data)
216 220

  
217 221

  
222
def DowngradeStorageTypes(cluster):
223
  # Remove storage types to downgrade to 2.7
224
  if "enabled_storage_types" in cluster:
225
    logging.warning("Removing cluster storage types; value = %s",
226
                    utils.CommaJoin(cluster["enabled_storage_types"]))
227
    del cluster["enabled_storage_types"]
228

  
229

  
230
def DowngradeCluster(config_data):
231
  cluster = config_data.get("cluster", None)
232
  if cluster is None:
233
    raise Error("Cannot find cluster")
234
  DowngradeStorageTypes(cluster)
235

  
236

  
237
def DowngradeAll(config_data):
238
  # Any code specific to a particular version should be labeled that way, so
239
  # it can be removed when updating to the next version.
240
  DowngradeCluster(config_data)
241

  
242

  
218 243
def main():
219 244
  """Main program.
220 245

  
......
243 268
  parser.add_option("--no-verify",
244 269
                    help="Do not verify configuration after upgrade",
245 270
                    action="store_true", dest="no_verify", default=False)
271
  parser.add_option("--downgrade",
272
                    help="Downgrade to the previous stable version",
273
                    action="store_true", dest="downgrade", default=False)
246 274
  (options, args) = parser.parse_args()
247 275

  
248 276
  # We need to keep filenames locally because they might be renamed between
......
267 295
  # Option checking
268 296
  if args:
269 297
    raise Error("No arguments expected")
298
  if options.downgrade and not options.no_verify:
299
    options.no_verify = True
270 300

  
271 301
  # Check master name
272 302
  if not (CheckHostname(options.SSCONF_MASTER_NODE) or options.ignore_hostname):
......
274 304
    sys.exit(constants.EXIT_FAILURE)
275 305

  
276 306
  if not options.force:
277
    usertext = ("Please make sure you have read the upgrade notes for"
278
                " Ganeti %s (available in the UPGRADE file and included"
279
                " in other documentation formats). Continue with upgrading"
280
                " configuration?" % constants.RELEASE_VERSION)
307
    if options.downgrade:
308
      usertext = ("The configuration is going to be DOWNGRADED to version %s.%s"
309
                  " Some configuration data might be removed if they don't fit"
310
                  " in the old format. Please make sure you have read the"
311
                  " upgrade notes (available in the UPGRADE file and included"
312
                  " in other documentation formats) to understand what they"
313
                  " are. Continue with *DOWNGRADING* the configuration?" %
314
                  (DOWNGRADE_MAJOR, DOWNGRADE_MINOR))
315
    else:
316
      usertext = ("Please make sure you have read the upgrade notes for"
317
                  " Ganeti %s (available in the UPGRADE file and included"
318
                  " in other documentation formats). Continue with upgrading"
319
                  " configuration?" % constants.RELEASE_VERSION)
281 320
    if not cli.AskUser(usertext):
282 321
      sys.exit(constants.EXIT_FAILURE)
283 322

  
......
308 347
    raise Error("Inconsistent configuration: found config_version in"
309 348
                " configuration file")
310 349

  
350
  # Downgrade to the previous stable version
351
  if options.downgrade:
352
    if config_major != TARGET_MAJOR or config_minor != TARGET_MINOR:
353
      raise Error("Downgrade supported only from the latest version (%s.%s),"
354
                  " found %s (%s.%s.%s) instead" %
355
                  (TARGET_MAJOR, TARGET_MINOR, config_version, config_major,
356
                   config_minor, config_revision))
357
    DowngradeAll(config_data)
358

  
311 359
  # Upgrade from 2.{0..6} to 2.7
312
  if config_major == 2 and config_minor in (0, 1, 2, 3, 4, 5, 6):
360
  elif config_major == 2 and config_minor in (0, 1, 2, 3, 4, 5, 6):
313 361
    if config_revision != 0:
314 362
      logging.warning("Config revision is %s, not 0", config_revision)
315 363
    UpgradeAll(config_data)
......
362 410
      logging.info("File loaded successfully after upgrading")
363 411
    del cfg
364 412

  
413
  if options.downgrade:
414
    action = "downgraded"
415
    out_ver = "%s.%s" % (DOWNGRADE_MAJOR, DOWNGRADE_MINOR)
416
  else:
417
    action = "upgraded"
418
    out_ver = constants.RELEASE_VERSION
365 419
  if all_ok:
366
    cli.ToStderr("Configuration successfully upgraded to version %s.",
367
                 constants.RELEASE_VERSION)
420
    cli.ToStderr("Configuration successfully %s to version %s.",
421
                 action, out_ver)
368 422
  else:
369
    cli.ToStderr("Configuration upgraded to version %s, but there are errors."
370
                 "\nPlease review the file.", constants.RELEASE_VERSION)
423
    cli.ToStderr("Configuration %s to version %s, but there are errors."
424
                 "\nPlease review the file.", action, out_ver)
371 425

  
372 426

  
373 427
if __name__ == "__main__":

Also available in: Unified diff