Remove constant for disk wipe block size
[ganeti-local] / qa / qa_config.py
index 62b0a07..635aec8 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2007, 2011 Google Inc.
+# Copyright (C) 2007, 2011, 2012 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -23,7 +23,9 @@
 
 """
 
+import os
 
+from ganeti import constants
 from ganeti import utils
 from ganeti import serializer
 from ganeti import compat
@@ -31,6 +33,10 @@ from ganeti import compat
 import qa_error
 
 
+_INSTANCE_CHECK_KEY = "instance-check"
+_ENABLED_HV_KEY = "enabled-hypervisors"
+
+
 cfg = None
 options = None
 
@@ -39,7 +45,7 @@ def Load(path):
   """Loads the passed configuration file.
 
   """
-  global cfg # pylint: disable-msg=W0603
+  global cfg # pylint: disable=W0603
 
   cfg = serializer.LoadJson(utils.ReadFile(path))
 
@@ -47,32 +53,155 @@ def Load(path):
 
 
 def Validate():
-  if len(cfg['nodes']) < 1:
+  if len(cfg["nodes"]) < 1:
     raise qa_error.Error("Need at least one node")
-  if len(cfg['instances']) < 1:
+  if len(cfg["instances"]) < 1:
     raise qa_error.Error("Need at least one instance")
   if len(cfg["disk"]) != len(cfg["disk-growth"]):
     raise qa_error.Error("Config options 'disk' and 'disk-growth' must have"
                          " the same number of items")
 
+  check = GetInstanceCheckScript()
+  if check:
+    try:
+      os.stat(check)
+    except EnvironmentError, err:
+      raise qa_error.Error("Can't find instance check script '%s': %s" %
+                           (check, err))
+
+  enabled_hv = frozenset(GetEnabledHypervisors())
+  if not enabled_hv:
+    raise qa_error.Error("No hypervisor is enabled")
+
+  difference = enabled_hv - constants.HYPER_TYPES
+  if difference:
+    raise qa_error.Error("Unknown hypervisor(s) enabled: %s" %
+                         utils.CommaJoin(difference))
+
 
 def get(name, default=None):
-  return cfg.get(name, default)
+  return cfg.get(name, default) # pylint: disable=E1103
+
+
+class Either:
+  def __init__(self, tests):
+    """Initializes this class.
+
+    @type tests: list or string
+    @param tests: List of test names
+    @see: L{TestEnabled} for details
+
+    """
+    self.tests = tests
+
+
+def _MakeSequence(value):
+  """Make sequence of single argument.
+
+  If the single argument is not already a list or tuple, a list with the
+  argument as a single item is returned.
+
+  """
+  if isinstance(value, (list, tuple)):
+    return value
+  else:
+    return [value]
+
+
+def _TestEnabledInner(check_fn, names, fn):
+  """Evaluate test conditions.
+
+  @type check_fn: callable
+  @param check_fn: Callback to check whether a test is enabled
+  @type names: sequence or string
+  @param names: Test name(s)
+  @type fn: callable
+  @param fn: Aggregation function
+  @rtype: bool
+  @return: Whether test is enabled
+
+  """
+  names = _MakeSequence(names)
+
+  result = []
+
+  for name in names:
+    if isinstance(name, Either):
+      value = _TestEnabledInner(check_fn, name.tests, compat.any)
+    elif isinstance(name, (list, tuple)):
+      value = _TestEnabledInner(check_fn, name, compat.all)
+    else:
+      value = check_fn(name)
+
+    result.append(value)
 
+  return fn(result)
 
-def TestEnabled(tests):
+
+def TestEnabled(tests, _cfg=None):
   """Returns True if the given tests are enabled.
 
-  @param tests: a single test, or a list of tests to check
+  @param tests: A single test as a string, or a list of tests to check; can
+    contain L{Either} for OR conditions, AND is default
+
+  """
+  if _cfg is None:
+    _cfg = cfg
+
+  # Get settings for all tests
+  cfg_tests = _cfg.get("tests", {}) # pylint: disable=E1103
+
+  # Get default setting
+  default = cfg_tests.get("default", True)
+
+  return _TestEnabledInner(lambda name: cfg_tests.get(name, default),
+                           tests, compat.all)
+
+
+def GetInstanceCheckScript():
+  """Returns path to instance check script or C{None}.
+
+  """
+  return cfg.get(_INSTANCE_CHECK_KEY, None) # pylint: disable=E1103
+
+
+def GetEnabledHypervisors():
+  """Returns list of enabled hypervisors.
+
+  @rtype: list
+
+  """
+  try:
+    value = cfg[_ENABLED_HV_KEY]
+  except KeyError:
+    return [constants.DEFAULT_ENABLED_HYPERVISOR]
+  else:
+    if isinstance(value, basestring):
+      # The configuration key ("enabled-hypervisors") implies there can be
+      # multiple values. Multiple hypervisors are comma-separated on the
+      # command line option to "gnt-cluster init", so we need to handle them
+      # equally here.
+      return value.split(",")
+    else:
+      return value
+
+
+def GetDefaultHypervisor():
+  """Returns the default hypervisor to be used.
+
+  """
+  return GetEnabledHypervisors()[0]
+
+
+def GetInstanceNicMac(inst, default=None):
+  """Returns MAC address for instance's network interface.
 
   """
-  if isinstance(tests, basestring):
-    tests = [tests]
-  return compat.all(cfg.get("tests", {}).get(t, True) for t in tests)
+  return inst.get("nic.mac/0", default)
 
 
 def GetMasterNode():
-  return cfg['nodes'][0]
+  return cfg["nodes"][0]
 
 
 def AcquireInstance():
@@ -80,20 +209,20 @@ def AcquireInstance():
 
   """
   # Filter out unwanted instances
-  tmp_flt = lambda inst: not inst.get('_used', False)
-  instances = filter(tmp_flt, cfg['instances'])
+  tmp_flt = lambda inst: not inst.get("_used", False)
+  instances = filter(tmp_flt, cfg["instances"])
   del tmp_flt
 
   if len(instances) == 0:
     raise qa_error.OutOfInstancesError("No instances left")
 
   inst = instances[0]
-  inst['_used'] = True
+  inst["_used"] = True
   return inst
 
 
 def ReleaseInstance(inst):
-  inst['_used'] = False
+  inst["_used"] = False
 
 
 def AcquireNode(exclude=None):
@@ -105,13 +234,13 @@ def AcquireNode(exclude=None):
   # Filter out unwanted nodes
   # TODO: Maybe combine filters
   if exclude is None:
-    nodes = cfg['nodes'][:]
+    nodes = cfg["nodes"][:]
   elif isinstance(exclude, (list, tuple)):
-    nodes = filter(lambda node: node not in exclude, cfg['nodes'])
+    nodes = filter(lambda node: node not in exclude, cfg["nodes"])
   else:
-    nodes = filter(lambda node: node != exclude, cfg['nodes'])
+    nodes = filter(lambda node: node != exclude, cfg["nodes"])
 
-  tmp_flt = lambda node: node.get('_added', False) or node == master
+  tmp_flt = lambda node: node.get("_added", False) or node == master
   nodes = filter(tmp_flt, nodes)
   del tmp_flt
 
@@ -120,17 +249,17 @@ def AcquireNode(exclude=None):
 
   # Get node with least number of uses
   def compare(a, b):
-    result = cmp(a.get('_count', 0), b.get('_count', 0))
+    result = cmp(a.get("_count", 0), b.get("_count", 0))
     if result == 0:
-      result = cmp(a['primary'], b['primary'])
+      result = cmp(a["primary"], b["primary"])
     return result
 
   nodes.sort(cmp=compare)
 
   node = nodes[0]
-  node['_count'] = node.get('_count', 0) + 1
+  node["_count"] = node.get("_count", 0) + 1
   return node
 
 
 def ReleaseNode(node):
-  node['_count'] = node.get('_count', 0) - 1
+  node["_count"] = node.get("_count", 0) - 1