Commit 76ae1d65 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Merge branch 'stable-2.4' into devel-2.4



* stable-2.4:
  Add error checking and merging for cluster params
  Clarify --force-join parameter message
  Treat empty oob_program param as default
  Fix bug in instance listing with orphan instances
  Fix bug related to log opening failures
  Bump version for 2.4.1 release
  cfgupgrade: Fix critical bug overwriting RAPI users file
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parents dae661a4 a6c8fd10
News
====
Version 2.4.1
-------------
*(Released Wed, 09 Mar 2011)*
Emergency bug-fix release. ``tools/cfgupgrade`` was broken and overwrote
the RAPI users file if run twice (even with ``-dry-run``).
The release fixes that bug (nothing else changed).
Version 2.4.0
-------------
......
# Configure script for Ganeti
m4_define([gnt_version_major], [2])
m4_define([gnt_version_minor], [4])
m4_define([gnt_version_revision], [0])
m4_define([gnt_version_revision], [1])
m4_define([gnt_version_suffix], [])
m4_define([gnt_version_full],
m4_format([%d.%d.%d%s],
......
......@@ -888,8 +888,7 @@ NOSSH_KEYCHECK_OPT = cli_option("--no-ssh-key-check", dest="ssh_key_check",
NODE_FORCE_JOIN_OPT = cli_option("--force-join", dest="force_join",
default=False, action="store_true",
help="Force the joining of a node,"
" needed when merging clusters")
help="Force the joining of a node")
MC_OPT = cli_option("-C", "--master-candidate", dest="master_candidate",
type="bool", default=None, metavar=_YORNO,
......
......@@ -2802,6 +2802,12 @@ class LUClusterSetParams(LogicalUnit):
utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
self.new_ndparams = cluster.SimpleFillND(self.op.ndparams)
# TODO: we need a more general way to handle resetting
# cluster-level parameters to default values
if self.new_ndparams["oob_program"] == "":
self.new_ndparams["oob_program"] = \
constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM]
if self.op.nicparams:
utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
self.new_nicparams = cluster.SimpleFillNIC(self.op.nicparams)
......@@ -3911,10 +3917,16 @@ class _InstanceQuery(_QueryBase):
bad_nodes.append(name)
elif result.payload:
for inst in result.payload:
if all_info[inst].primary_node == name:
live_data.update(result.payload)
if inst in all_info:
if all_info[inst].primary_node == name:
live_data.update(result.payload)
else:
wrongnode_inst.add(inst)
else:
wrongnode_inst.add(inst)
# orphan instance; we don't list it here as we don't
# handle this case yet in the output of instance listing
logging.warning("Orphan instance '%s' found on node %s",
inst, name)
# else no instance is alive
else:
live_data = {}
......
......@@ -245,9 +245,18 @@ def SetupLogging(logfile, program, debug=0, stderr_logging=False,
# exception since otherwise we could run but without any logs at all
try:
if console_logging:
logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"), logfile)
logfile_handler = _LogHandler(open(constants.DEV_CONSOLE, "a"),
logfile)
else:
logfile_handler = _ReopenableLogHandler(logfile)
logfile_handler.setFormatter(formatter)
if debug:
logfile_handler.setLevel(logging.DEBUG)
else:
logfile_handler.setLevel(logging.INFO)
root_logger.addHandler(logfile_handler)
reopen_handlers.append(logfile_handler)
except EnvironmentError:
if stderr_logging or syslog == constants.SYSLOG_YES:
logging.exception("Failed to enable logging to file '%s'", logfile)
......@@ -255,13 +264,4 @@ def SetupLogging(logfile, program, debug=0, stderr_logging=False,
# we need to re-raise the exception
raise
logfile_handler.setFormatter(formatter)
if debug:
logfile_handler.setLevel(logging.DEBUG)
else:
logfile_handler.setLevel(logging.INFO)
root_logger.addHandler(logfile_handler)
reopen_handlers.append(logfile_handler)
return compat.partial(_ReopenLogFiles, reopen_handlers)
......@@ -161,12 +161,16 @@ class TestCfgupgrade(unittest.TestCase):
def testRapiUsers(self):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
self.assertFalse(os.path.exists(os.path.dirname(self.rapi_users_path)))
utils.WriteFile(self.rapi_users_path_pre24, data="some user\n")
self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), False)
self.assertTrue(os.path.isdir(os.path.dirname(self.rapi_users_path)))
self.assert_(os.path.islink(self.rapi_users_path_pre24))
self.assert_(os.path.isfile(self.rapi_users_path))
self.assertEqual(os.readlink(self.rapi_users_path_pre24),
self.rapi_users_path)
for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
self.assertEqual(utils.ReadFile(path), "some user\n")
......@@ -180,6 +184,8 @@ class TestCfgupgrade(unittest.TestCase):
self.assert_(os.path.islink(self.rapi_users_path_pre24))
self.assert_(os.path.isfile(self.rapi_users_path))
self.assertEqual(os.readlink(self.rapi_users_path_pre24),
self.rapi_users_path)
for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
self.assertEqual(utils.ReadFile(path), "other user\n")
......@@ -187,13 +193,77 @@ class TestCfgupgrade(unittest.TestCase):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
os.mkdir(os.path.dirname(self.rapi_users_path))
os.symlink(self.rapi_users_path, self.rapi_users_path_pre24)
utils.WriteFile(self.rapi_users_path_pre24, data="hello world\n")
utils.WriteFile(self.rapi_users_path, data="hello world\n")
self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), False)
self.assert_(os.path.isfile(self.rapi_users_path))
self.assert_(os.path.isfile(self.rapi_users_path) and
not os.path.islink(self.rapi_users_path))
self.assert_(os.path.islink(self.rapi_users_path_pre24))
self.assertEqual(os.readlink(self.rapi_users_path_pre24),
self.rapi_users_path)
for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
self.assertEqual(utils.ReadFile(path), "hello world\n")
def testRapiUsersExistingTarget(self):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
os.mkdir(os.path.dirname(self.rapi_users_path))
utils.WriteFile(self.rapi_users_path, data="other user\n")
utils.WriteFile(self.rapi_users_path_pre24, data="hello world\n")
self.assertRaises(Exception, self._TestSimpleUpgrade,
constants.BuildVersion(2, 2, 0), False)
for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
self.assert_(os.path.isfile(path) and not os.path.islink(path))
self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n")
self.assertEqual(utils.ReadFile(self.rapi_users_path_pre24),
"hello world\n")
def testRapiUsersDryRun(self):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
utils.WriteFile(self.rapi_users_path_pre24, data="some user\n")
self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), True)
self.assertFalse(os.path.isdir(os.path.dirname(self.rapi_users_path)))
self.assertTrue(os.path.isfile(self.rapi_users_path_pre24) and
not os.path.islink(self.rapi_users_path_pre24))
self.assertFalse(os.path.exists(self.rapi_users_path))
def testRapiUsers24AndAboveDryRun(self):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
os.mkdir(os.path.dirname(self.rapi_users_path))
utils.WriteFile(self.rapi_users_path, data="other user\n")
self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), True)
self.assertTrue(os.path.isfile(self.rapi_users_path) and
not os.path.islink(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n")
def testRapiUsersExistingSymlinkDryRun(self):
self.assertFalse(os.path.exists(self.rapi_users_path))
self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
os.mkdir(os.path.dirname(self.rapi_users_path))
os.symlink(self.rapi_users_path, self.rapi_users_path_pre24)
utils.WriteFile(self.rapi_users_path, data="hello world\n")
self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), True)
self.assertTrue(os.path.islink(self.rapi_users_path_pre24))
self.assertTrue(os.path.isfile(self.rapi_users_path) and
not os.path.islink(self.rapi_users_path))
self.assertEqual(os.readlink(self.rapi_users_path_pre24),
self.rapi_users_path)
for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
self.assertEqual(utils.ReadFile(path), "hello world\n")
......
......@@ -185,17 +185,26 @@ def main():
raise Error("Configuration version %d.%d.%d not supported by this tool" %
(config_major, config_minor, config_revision))
if os.path.isfile(options.RAPI_USERS_FILE_PRE24):
if (os.path.isfile(options.RAPI_USERS_FILE_PRE24) and
not os.path.islink(options.RAPI_USERS_FILE_PRE24)):
if os.path.exists(options.RAPI_USERS_FILE):
raise Error("Found pre-2.4 RAPI users file at %s, but another file"
" already exists at %s" %
(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE))
logging.info("Found pre-2.4 RAPI users file at %s, renaming to %s",
options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
mkdir=True, mkdir_mode=0750)
if not options.dry_run:
utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
mkdir=True, mkdir_mode=0750)
# Create a symlink for RAPI users file
if not os.path.islink(options.RAPI_USERS_FILE_PRE24):
if (not (os.path.islink(options.RAPI_USERS_FILE_PRE24) or
os.path.isfile(options.RAPI_USERS_FILE_PRE24)) and
os.path.isfile(options.RAPI_USERS_FILE)):
logging.info("Creating symlink from %s to %s",
options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
if not options.dry_run:
os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
try:
logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
......
......@@ -282,6 +282,7 @@ class Merger(object):
for data in self.merger_data:
other_config = config.ConfigWriter(data.config_path, accept_foreign=True)
self._MergeClusterConfigs(my_config, other_config)
self._MergeNodeGroups(my_config, other_config)
for node in other_config.GetNodeList():
......@@ -310,6 +311,145 @@ class Merger(object):
_CLUSTERMERGE_ECID + str(fake_ec_id))
fake_ec_id += 1
# R0201: Method could be a function
def _MergeClusterConfigs(self, my_config, other_config):
"""Checks that all relevant cluster parameters are compatible
"""
# pylint: disable-msg=R0201
my_cluster = my_config.GetClusterInfo()
other_cluster = other_config.GetClusterInfo()
err_count = 0
#
# Generic checks
#
check_params = (
"beparams",
"default_iallocator",
"drbd_usermode_helper",
"file_storage_dir",
"hidden_os",
"maintain_node_health",
"master_netdev",
"ndparams",
"nicparams",
"primary_ip_family",
"tags",
"uid_pool",
"volume_group_name",
)
for param_name in check_params:
my_param = getattr(my_cluster, param_name)
other_param = getattr(other_cluster, param_name)
if my_param != other_param:
logging.error("The value (%s) of the cluster parameter %s on %s"
" differs to this cluster's value (%s)",
other_param, param_name, other_cluster.cluster_name,
my_param)
err_count += 1
#
# Custom checks
#
# Check default hypervisor
my_defhyp = my_cluster.enabled_hypervisors[0]
other_defhyp = other_cluster.enabled_hypervisors[0]
if my_defhyp != other_defhyp:
logging.warning("The default hypervisor (%s) differs on %s, new"
" instances will be created with this cluster's"
" default hypervisor (%s)", other_defhyp,
other_cluster.cluster_name, my_defhyp)
if (set(my_cluster.enabled_hypervisors) !=
set(other_cluster.enabled_hypervisors)):
logging.error("The set of enabled hypervisors (%s) on %s differs to"
" this cluster's set (%s)",
other_cluster.enabled_hypervisors,
other_cluster.cluster_name, my_cluster.enabled_hypervisors)
err_count += 1
# Check hypervisor params for hypervisors we care about
# TODO: we probably don't care about all params for a given hypervisor
for hyp in my_cluster.enabled_hypervisors:
for param in my_cluster.hvparams[hyp]:
my_value = my_cluster.hvparams[hyp][param]
other_value = other_cluster.hvparams[hyp][param]
if my_value != other_value:
logging.error("The value (%s) of the %s parameter of the %s"
" hypervisor on %s differs to this cluster's parameter"
" (%s)",
other_value, param, hyp, other_cluster.cluster_name,
my_value)
err_count += 1
# Check os hypervisor params for hypervisors we care about
for os_name in set(my_cluster.os_hvp.keys() + other_cluster.os_hvp.keys()):
for hyp in my_cluster.enabled_hypervisors:
my_os_hvp = self._GetOsHypervisor(my_cluster, os_name, hyp)
other_os_hvp = self._GetOsHypervisor(other_cluster, os_name, hyp)
if my_os_hvp != other_os_hvp:
logging.error("The OS parameters (%s) for the %s OS for the %s"
" hypervisor on %s differs to this cluster's parameters"
" (%s)",
other_os_hvp, os_name, hyp, other_cluster.cluster_name,
my_os_hvp)
err_count += 1
#
# Warnings
#
if my_cluster.modify_etc_hosts != other_cluster.modify_etc_hosts:
logging.warning("The modify_etc_hosts value (%s) differs on %s,"
" this cluster's value (%s) will take precedence",
other_cluster.modify_etc_hosts,
other_cluster.cluster_name,
my_cluster.modify_etc_hosts)
if my_cluster.modify_ssh_setup != other_cluster.modify_ssh_setup:
logging.warning("The modify_ssh_setup value (%s) differs on %s,"
" this cluster's value (%s) will take precedence",
other_cluster.modify_ssh_setup,
other_cluster.cluster_name,
my_cluster.modify_ssh_setup)
#
# Actual merging
#
my_cluster.reserved_lvs = list(set(my_cluster.reserved_lvs +
other_cluster.reserved_lvs))
if my_cluster.prealloc_wipe_disks != other_cluster.prealloc_wipe_disks:
logging.warning("The prealloc_wipe_disks value (%s) on %s differs to this"
" cluster's value (%s). The least permissive value (%s)"
" will be used", other_cluster.prealloc_wipe_disks,
other_cluster.cluster_name,
my_cluster.prealloc_wipe_disks, True)
my_cluster.prealloc_wipe_disks = True
for os_, osparams in other_cluster.osparams.items():
if os_ not in my_cluster.osparams:
my_cluster.osparams[os_] = osparams
elif my_cluster.osparams[os_] != osparams:
logging.error("The OS parameters (%s) for the %s OS on %s differs to"
" this cluster's parameters (%s)",
osparams, os_, other_cluster.cluster_name,
my_cluster.osparams[os_])
err_count += 1
if err_count:
raise errors.ConfigurationError("Cluster config for %s has incompatible"
" values, please fix and re-run" %
other_cluster.cluster_name)
# R0201: Method could be a function
def _GetOsHypervisor(self, cluster, os_name, hyp): # pylint: disable-msg=R0201
if os_name in cluster.os_hvp:
return cluster.os_hvp[os_name].get(hyp, None)
else:
return None
# R0201: Method could be a function
def _MergeNodeGroups(self, my_config, other_config):
"""Adds foreign node groups
......@@ -536,7 +676,7 @@ def SetupLogging(options):
elif options.verbose:
stderr_handler.setLevel(logging.INFO)
else:
stderr_handler.setLevel(logging.ERROR)
stderr_handler.setLevel(logging.WARNING)
root_logger = logging.getLogger("")
root_logger.setLevel(logging.NOTSET)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment