+
+
+def AddToEtcHosts(hostnames):
+ """Adds hostnames to /etc/hosts.
+
+ @param hostnames: List of hostnames first used A records, all other CNAMEs
+
+ """
+ master = qa_config.GetMasterNode()
+ tmp_hosts = UploadData(master.primary, "", mode=0644)
+
+ data = []
+ for localhost in ("::1", "127.0.0.1"):
+ data.append("%s %s" % (localhost, " ".join(hostnames)))
+
+ try:
+ AssertCommand("{ cat %s && echo -e '%s'; } > %s && mv %s %s" %
+ (utils.ShellQuote(pathutils.ETC_HOSTS),
+ "\\n".join(data),
+ utils.ShellQuote(tmp_hosts),
+ utils.ShellQuote(tmp_hosts),
+ utils.ShellQuote(pathutils.ETC_HOSTS)))
+ except Exception:
+ AssertCommand(["rm", "-f", tmp_hosts])
+ raise
+
+
+def RemoveFromEtcHosts(hostnames):
+ """Remove hostnames from /etc/hosts.
+
+ @param hostnames: List of hostnames first used A records, all other CNAMEs
+
+ """
+ master = qa_config.GetMasterNode()
+ tmp_hosts = UploadData(master.primary, "", mode=0644)
+ quoted_tmp_hosts = utils.ShellQuote(tmp_hosts)
+
+ sed_data = " ".join(hostnames)
+ try:
+ AssertCommand(("sed -e '/^\(::1\|127\.0\.0\.1\)\s\+%s/d' %s > %s"
+ " && mv %s %s") %
+ (sed_data, utils.ShellQuote(pathutils.ETC_HOSTS),
+ quoted_tmp_hosts, quoted_tmp_hosts,
+ utils.ShellQuote(pathutils.ETC_HOSTS)))
+ except Exception:
+ AssertCommand(["rm", "-f", tmp_hosts])
+ raise
+
+
+def RunInstanceCheck(instance, running):
+ """Check if instance is running or not.
+
+ """
+ instance_name = _GetName(instance, operator.attrgetter("name"))
+
+ script = qa_config.GetInstanceCheckScript()
+ if not script:
+ return
+
+ master_node = qa_config.GetMasterNode()
+
+ # Build command to connect to master node
+ master_ssh = GetSSHCommand(master_node.primary, "--")
+
+ if running:
+ running_shellval = "1"
+ running_text = ""
+ else:
+ running_shellval = ""
+ running_text = "not "
+
+ print FormatInfo("Checking if instance '%s' is %srunning" %
+ (instance_name, running_text))
+
+ args = [script, instance_name]
+ env = {
+ "PATH": constants.HOOKS_PATH,
+ "RUN_UUID": _RUN_UUID,
+ "MASTER_SSH": utils.ShellQuoteArgs(master_ssh),
+ "INSTANCE_NAME": instance_name,
+ "INSTANCE_RUNNING": running_shellval,
+ }
+
+ result = os.spawnve(os.P_WAIT, script, args, env)
+ if result != 0:
+ raise qa_error.Error("Instance check failed with result %s" % result)
+
+
+def _InstanceCheckInner(expected, instarg, args, result):
+ """Helper function used by L{InstanceCheck}.
+
+ """
+ if instarg == FIRST_ARG:
+ instance = args[0]
+ elif instarg == RETURN_VALUE:
+ instance = result
+ else:
+ raise Exception("Invalid value '%s' for instance argument" % instarg)
+
+ if expected in (INST_DOWN, INST_UP):
+ RunInstanceCheck(instance, (expected == INST_UP))
+ elif expected is not None:
+ raise Exception("Invalid value '%s'" % expected)
+
+
+def InstanceCheck(before, after, instarg):
+ """Decorator to check instance status before and after test.
+
+ @param before: L{INST_DOWN} if instance must be stopped before test,
+ L{INST_UP} if instance must be running before test, L{None} to not check.
+ @param after: L{INST_DOWN} if instance must be stopped after test,
+ L{INST_UP} if instance must be running after test, L{None} to not check.
+ @param instarg: L{FIRST_ARG} to use first argument to test as instance (a
+ dictionary), L{RETURN_VALUE} to use return value (disallows pre-checks)
+
+ """
+ def decorator(fn):
+ @functools.wraps(fn)
+ def wrapper(*args, **kwargs):
+ _InstanceCheckInner(before, instarg, args, NotImplemented)
+
+ result = fn(*args, **kwargs)
+
+ _InstanceCheckInner(after, instarg, args, result)
+
+ return result
+ return wrapper
+ return decorator
+
+
+def GetNonexistentGroups(count):
+ """Gets group names which shouldn't exist on the cluster.
+
+ @param count: Number of groups to get
+ @rtype: integer
+
+ """
+ return GetNonexistentEntityNames(count, "groups", "group")
+
+
+def GetNonexistentEntityNames(count, name_config, name_prefix):
+ """Gets entity names which shouldn't exist on the cluster.
+
+ The actualy names can refer to arbitrary entities (for example
+ groups, networks).
+
+ @param count: Number of names to get
+ @rtype: integer
+ @param name_config: name of the leaf in the config containing
+ this entity's configuration, including a 'inexistent-'
+ element
+ @rtype: string
+ @param name_prefix: prefix of the entity's names, used to compose
+ the default values; for example for groups, the prefix is
+ 'group' and the generated names are then group1, group2, ...
+ @rtype: string
+
+ """
+ entities = qa_config.get(name_config, {})
+
+ default = [name_prefix + str(i) for i in range(count)]
+ assert count <= len(default)
+
+ name_config_inexistent = "inexistent-" + name_config
+ candidates = entities.get(name_config_inexistent, default)[:count]
+
+ if len(candidates) < count:
+ raise Exception("At least %s non-existent %s are needed" %
+ (count, name_config))
+
+ return candidates
+
+
+def MakeNodePath(node, path):
+ """Builds an absolute path for a virtual node.
+
+ @type node: string or L{qa_config._QaNode}
+ @param node: Node
+ @type path: string
+ @param path: Path without node-specific prefix
+
+ """
+ (_, basedir) = qa_config.GetVclusterSettings()
+
+ if isinstance(node, basestring):
+ name = node
+ else:
+ name = node.primary
+
+ if basedir:
+ assert path.startswith("/")
+ return "%s%s" % (vcluster.MakeNodeRoot(basedir, name), path)
+ else:
+ return path
+
+
+def _GetParameterOptions(key, specs, old_specs):
+ """Helper to build policy options."""
+ values = ["%s=%s" % (par, keyvals[key])
+ for (par, keyvals) in specs.items()
+ if key in keyvals]
+ if old_specs:
+ present_pars = frozenset(par
+ for (par, keyvals) in specs.items()
+ if key in keyvals)
+ values.extend("%s=%s" % (par, keyvals[key])
+ for (par, keyvals) in old_specs.items()
+ if key in keyvals and par not in present_pars)
+ return ",".join(values)
+
+
+def TestSetISpecs(new_specs, get_policy_fn=None, build_cmd_fn=None,
+ fail=False, old_values=None):
+ """Change instance specs for an object.
+
+ @type new_specs: dict of dict
+ @param new_specs: new_specs[par][key], where key is "min", "max", "std". It
+ can be an empty dictionary.
+ @type get_policy_fn: function
+ @param get_policy_fn: function that returns the current policy as in
+ L{qa_cluster._GetClusterIPolicy}
+ @type build_cmd_fn: function
+ @param build_cmd_fn: function that return the full command line from the
+ options alone
+ @type fail: bool
+ @param fail: if the change is expected to fail
+ @type old_values: tuple
+ @param old_values: (old_policy, old_specs), as returned by
+ L{qa_cluster._GetClusterIPolicy}
+ @return: same as L{qa_cluster._GetClusterIPolicy}
+
+ """
+ assert get_policy_fn is not None
+ assert build_cmd_fn is not None
+
+ if old_values:
+ (old_policy, old_specs) = old_values
+ else:
+ (old_policy, old_specs) = get_policy_fn()
+ if new_specs:
+ cmd = []
+ if any(("min" in val or "max" in val) for val in new_specs.values()):
+ minmax_opt_items = []
+ for key in ["min", "max"]:
+ keyopt = _GetParameterOptions(key, new_specs, old_specs)
+ minmax_opt_items.append("%s:%s" % (key, keyopt))
+ cmd.extend([
+ "--ipolicy-bounds-specs",
+ "/".join(minmax_opt_items)
+ ])
+ std_opt = _GetParameterOptions("std", new_specs, {})
+ if std_opt:
+ cmd.extend(["--ipolicy-std-specs", std_opt])
+ AssertCommand(build_cmd_fn(cmd), fail=fail)
+
+ # Check the new state
+ (eff_policy, eff_specs) = get_policy_fn()
+ AssertEqual(eff_policy, old_policy)
+ if fail:
+ AssertEqual(eff_specs, old_specs)
+ else:
+ for par in eff_specs:
+ for key in eff_specs[par]:
+ if par in new_specs and key in new_specs[par]:
+ AssertEqual(int(eff_specs[par][key]), int(new_specs[par][key]))
+ else:
+ AssertEqual(int(eff_specs[par][key]), int(old_specs[par][key]))
+ return (eff_policy, eff_specs)
+
+
+def ParseIPolicy(policy):
+ """Parse and split instance an instance policy.
+
+ @type policy: dict
+ @param policy: policy, as returned by L{GetObjectInfo}
+ @rtype: tuple
+ @return: (policy, specs), where:
+ - policy is a dictionary of the policy values, instance specs excluded
+ - specs is dict of dict, specs[par][key] is a spec value, where key is
+ "min", "max", or "std"
+
+ """
+ ret_specs = {}
+ ret_policy = {}
+ ispec_keys = constants.ISPECS_MINMAX_KEYS | frozenset([constants.ISPECS_STD])
+ for (key, val) in policy.items():
+ if key in ispec_keys:
+ for (par, pval) in val.items():
+ d = ret_specs.setdefault(par, {})
+ d[key] = pval
+ else:
+ ret_policy[key] = val
+ return (ret_policy, ret_specs)