_INSTANCE_CHECK_KEY = "instance-check"
_ENABLED_HV_KEY = "enabled-hypervisors"
-# Key to store the cluster-wide run-time value of the exclusive storage flag
-_EXCLUSIVE_STORAGE_KEY = "_exclusive_storage"
+#: QA configuration (L{_QaConfig})
+_config = None
-cfg = {}
-options = None
+class _QaInstance(object):
+ __slots__ = [
+ "name",
+ "nicmac",
+ "used",
+ "disk_template",
+ ]
-def Load(path):
- """Loads the passed configuration file.
+ def __init__(self, name, nicmac):
+ """Initializes instances of this class.
+
+ """
+ self.name = name
+ self.nicmac = nicmac
+ self.used = None
+ self.disk_template = None
+
+ @classmethod
+ def FromDict(cls, data):
+ """Creates instance object from JSON dictionary.
+
+ """
+ nicmac = []
+
+ macaddr = data.get("nic.mac/0")
+ if macaddr:
+ nicmac.append(macaddr)
+
+ return cls(name=data["name"], nicmac=nicmac)
+
+ def __getitem__(self, key):
+ """Legacy dict-like interface.
+
+ """
+ if key == "name":
+ return self.name
+ else:
+ raise KeyError(key)
+
+ def get(self, key, default):
+ """Legacy dict-like interface.
+
+ """
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def GetNicMacAddr(self, idx, default):
+ """Returns MAC address for NIC.
+
+ @type idx: int
+ @param idx: NIC index
+ @param default: Default value
+
+ """
+ if len(self.nicmac) > idx:
+ return self.nicmac[idx]
+ else:
+ return default
+
+
+_RESOURCE_CONVERTER = {
+ "instances": _QaInstance.FromDict,
+ }
+
+
+def _ConvertResources((key, value)):
+ """Converts cluster resources in configuration to Python objects.
"""
- global cfg # pylint: disable=W0603
+ fn = _RESOURCE_CONVERTER.get(key, None)
+ if fn:
+ return (key, map(fn, value))
+ else:
+ return (key, value)
+
+
+class _QaConfig(object):
+ def __init__(self, data):
+ """Initializes instances of this class.
+
+ """
+ self._data = data
+
+ #: Cluster-wide run-time value of the exclusive storage flag
+ self._exclusive_storage = None
+
+ @classmethod
+ def Load(cls, filename):
+ """Loads a configuration file and produces a configuration object.
+
+ @type filename: string
+ @param filename: Path to configuration file
+ @rtype: L{_QaConfig}
+
+ """
+ data = serializer.LoadJson(utils.ReadFile(filename))
+
+ result = cls(dict(map(_ConvertResources,
+ data.items()))) # pylint: disable=E1103
+ result.Validate()
+
+ return result
- cfg = serializer.LoadJson(utils.ReadFile(path))
+ def Validate(self):
+ """Validates loaded configuration data.
- Validate()
+ """
+ if not self.get("nodes"):
+ raise qa_error.Error("Need at least one node")
+
+ if not self.get("instances"):
+ raise qa_error.Error("Need at least one instance")
+
+ if (self.get("disk") is None or
+ self.get("disk-growth") is None or
+ len(self.get("disk")) != len(self.get("disk-growth"))):
+ raise qa_error.Error("Config options 'disk' and 'disk-growth' must exist"
+ " and have the same number of items")
+
+ check = self.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(self.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 __getitem__(self, name):
+ """Returns configuration value.
+ @type name: string
+ @param name: Name of configuration entry
+
+ """
+ return self._data[name]
+
+ def get(self, name, default=None):
+ """Returns configuration value.
+
+ @type name: string
+ @param name: Name of configuration entry
+ @param default: Default value
+
+ """
+ return self._data.get(name, default)
-def Validate():
- if len(cfg["nodes"]) < 1:
- raise qa_error.Error("Need at least one node")
- 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")
+ def GetMasterNode(self):
+ """Returns the default master node for the cluster.
- check = GetInstanceCheckScript()
- if check:
+ """
+ return self["nodes"][0]
+
+ def GetInstanceCheckScript(self):
+ """Returns path to instance check script or C{None}.
+
+ """
+ return self._data.get(_INSTANCE_CHECK_KEY, None)
+
+ def GetEnabledHypervisors(self):
+ """Returns list of enabled hypervisors.
+
+ @rtype: list
+
+ """
try:
- os.stat(check)
- except EnvironmentError, err:
- raise qa_error.Error("Can't find instance check script '%s': %s" %
- (check, err))
+ value = self._data[_ENABLED_HV_KEY]
+ except KeyError:
+ return [constants.DEFAULT_ENABLED_HYPERVISOR]
+ else:
+ if value is None:
+ return []
+ elif 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(self):
+ """Returns the default hypervisor to be used.
+
+ """
+ return self.GetEnabledHypervisors()[0]
+
+ def SetExclusiveStorage(self, value):
+ """Set the expected value of the C{exclusive_storage} flag for the cluster.
+
+ """
+ self._exclusive_storage = bool(value)
+
+ def GetExclusiveStorage(self):
+ """Get the expected value of the C{exclusive_storage} flag for the cluster.
- enabled_hv = frozenset(GetEnabledHypervisors())
- if not enabled_hv:
- raise qa_error.Error("No hypervisor is enabled")
+ """
+ value = self._exclusive_storage
+ assert value is not None
+ return value
+
+ def IsTemplateSupported(self, templ):
+ """Is the given disk template supported by the current configuration?
+
+ """
+ return (not self.GetExclusiveStorage() or
+ templ in constants.DTS_EXCL_STORAGE)
+
+
+def Load(path):
+ """Loads the passed configuration file.
+
+ """
+ global _config # pylint: disable=W0603
- difference = enabled_hv - constants.HYPER_TYPES
- if difference:
- raise qa_error.Error("Unknown hypervisor(s) enabled: %s" %
- utils.CommaJoin(difference))
+ _config = _QaConfig.Load(path)
+
+
+def GetConfig():
+ """Returns the configuration object.
+
+ """
+ if _config is None:
+ raise RuntimeError("Configuration not yet loaded")
+
+ return _config
def get(name, default=None):
- return cfg.get(name, default)
+ """Wrapper for L{_QaConfig.get}.
+
+ """
+ return GetConfig().get(name, default=default)
class Either:
"""
if _cfg is None:
- _cfg = cfg
+ cfg = GetConfig()
+ else:
+ cfg = _cfg
# Get settings for all tests
- cfg_tests = _cfg.get("tests", {})
+ cfg_tests = cfg.get("tests", {})
# Get default setting
default = cfg_tests.get("default", True)
tests, compat.all)
-def GetInstanceCheckScript():
- """Returns path to instance check script or C{None}.
+def GetInstanceCheckScript(*args):
+ """Wrapper for L{_QaConfig.GetInstanceCheckScript}.
"""
- return cfg.get(_INSTANCE_CHECK_KEY, None)
-
+ return GetConfig().GetInstanceCheckScript(*args)
-def GetEnabledHypervisors():
- """Returns list of enabled hypervisors.
- @rtype: list
+def GetEnabledHypervisors(*args):
+ """Wrapper for L{_QaConfig.GetEnabledHypervisors}.
"""
- 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
+ return GetConfig().GetEnabledHypervisors(*args)
-def GetDefaultHypervisor():
- """Returns the default hypervisor to be used.
+def GetDefaultHypervisor(*args):
+ """Wrapper for L{_QaConfig.GetDefaultHypervisor}.
"""
- return GetEnabledHypervisors()[0]
+ return GetConfig().GetDefaultHypervisor(*args)
def GetInstanceNicMac(inst, default=None):
"""Returns MAC address for instance's network interface.
"""
- return inst.get("nic.mac/0", default)
+ return inst.GetNicMacAddr(0, default)
def GetMasterNode():
- return cfg["nodes"][0]
+ """Wrapper for L{_QaConfig.GetMasterNode}.
+ """
+ return GetConfig().GetMasterNode()
-def AcquireInstance():
+
+def AcquireInstance(_cfg=None):
"""Returns an instance which isn't in use.
"""
+ if _cfg is None:
+ cfg = GetConfig()
+ else:
+ cfg = _cfg
+
# Filter out unwanted instances
- tmp_flt = lambda inst: not inst.get("_used", False)
- instances = filter(tmp_flt, cfg["instances"])
- del tmp_flt
+ instances = filter(lambda inst: not inst.used, cfg["instances"])
- if len(instances) == 0:
+ if not instances:
raise qa_error.OutOfInstancesError("No instances left")
inst = instances[0]
- inst["_used"] = True
- inst["_template"] = None
+
+ assert not inst.used
+ assert inst.disk_template is None
+
+ inst.used = True
+
return inst
def ReleaseInstance(inst):
- inst["_used"] = False
+ inst.used = False
+ inst.disk_template = None
def GetInstanceTemplate(inst):
"""Return the disk template of an instance.
"""
- templ = inst["_template"]
+ templ = inst.disk_template
assert templ is not None
return templ
"""Set the disk template for an instance.
"""
- inst["_template"] = template
+ inst.disk_template = template
def SetExclusiveStorage(value):
- """Set the expected value of the exclusive_storage flag for the cluster.
+ """Wrapper for L{_QaConfig.SetExclusiveStorage}.
"""
- cfg[_EXCLUSIVE_STORAGE_KEY] = bool(value)
+ return GetConfig().SetExclusiveStorage(value)
def GetExclusiveStorage():
- """Get the expected value of the exclusive_storage flag for the cluster.
+ """Wrapper for L{_QaConfig.GetExclusiveStorage}.
+
+ """
+ return GetConfig().GetExclusiveStorage()
+
+
+def IsTemplateSupported(templ):
+ """Wrapper for L{_QaConfig.GetExclusiveStorage}.
"""
- val = cfg.get(_EXCLUSIVE_STORAGE_KEY)
- assert val is not None
- return val
+ return GetConfig().IsTemplateSupported(templ)
def AcquireNode(exclude=None):
"""
master = GetMasterNode()
+ cfg = GetConfig()
# Filter out unwanted nodes
# TODO: Maybe combine filters