4 # Copyright (C) 2007, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 from ganeti import constants
29 from ganeti import utils
30 from ganeti import serializer
31 from ganeti import compat
36 _INSTANCE_CHECK_KEY = "instance-check"
37 _ENABLED_HV_KEY = "enabled-hypervisors"
45 """Loads the passed configuration file.
48 global cfg # pylint: disable=W0603
50 cfg = serializer.LoadJson(utils.ReadFile(path))
56 if len(cfg["nodes"]) < 1:
57 raise qa_error.Error("Need at least one node")
58 if len(cfg["instances"]) < 1:
59 raise qa_error.Error("Need at least one instance")
60 if len(cfg["disk"]) != len(cfg["disk-growth"]):
61 raise qa_error.Error("Config options 'disk' and 'disk-growth' must have"
62 " the same number of items")
64 check = GetInstanceCheckScript()
68 except EnvironmentError, err:
69 raise qa_error.Error("Can't find instance check script '%s': %s" %
72 enabled_hv = frozenset(GetEnabledHypervisors())
74 raise qa_error.Error("No hypervisor is enabled")
76 difference = enabled_hv - constants.HYPER_TYPES
78 raise qa_error.Error("Unknown hypervisor(s) enabled: %s" %
79 utils.CommaJoin(difference))
82 def get(name, default=None):
83 return cfg.get(name, default) # pylint: disable=E1103
87 def __init__(self, tests):
88 """Initializes this class.
90 @type tests: list or string
91 @param tests: List of test names
92 @see: L{TestEnabled} for details
98 def _MakeSequence(value):
99 """Make sequence of single argument.
101 If the single argument is not already a list or tuple, a list with the
102 argument as a single item is returned.
105 if isinstance(value, (list, tuple)):
111 def _TestEnabledInner(check_fn, names, fn):
112 """Evaluate test conditions.
114 @type check_fn: callable
115 @param check_fn: Callback to check whether a test is enabled
116 @type names: sequence or string
117 @param names: Test name(s)
119 @param fn: Aggregation function
121 @return: Whether test is enabled
124 names = _MakeSequence(names)
129 if isinstance(name, Either):
130 value = _TestEnabledInner(check_fn, name.tests, compat.any)
131 elif isinstance(name, (list, tuple)):
132 value = _TestEnabledInner(check_fn, name, compat.all)
134 value = check_fn(name)
141 def TestEnabled(tests, _cfg=None):
142 """Returns True if the given tests are enabled.
144 @param tests: A single test as a string, or a list of tests to check; can
145 contain L{Either} for OR conditions, AND is default
151 # Get settings for all tests
152 cfg_tests = _cfg.get("tests", {}) # pylint: disable=E1103
154 # Get default setting
155 default = cfg_tests.get("default", True)
157 return _TestEnabledInner(lambda name: cfg_tests.get(name, default),
161 def GetInstanceCheckScript():
162 """Returns path to instance check script or C{None}.
165 return cfg.get(_INSTANCE_CHECK_KEY, None) # pylint: disable=E1103
168 def GetEnabledHypervisors():
169 """Returns list of enabled hypervisors.
175 value = cfg[_ENABLED_HV_KEY]
177 return [constants.DEFAULT_ENABLED_HYPERVISOR]
179 if isinstance(value, basestring):
180 # The configuration key ("enabled-hypervisors") implies there can be
181 # multiple values. Multiple hypervisors are comma-separated on the
182 # command line option to "gnt-cluster init", so we need to handle them
184 return value.split(",")
189 def GetDefaultHypervisor():
190 """Returns the default hypervisor to be used.
193 return GetEnabledHypervisors()[0]
196 def GetInstanceNicMac(inst, default=None):
197 """Returns MAC address for instance's network interface.
200 return inst.get("nic.mac/0", default)
204 return cfg["nodes"][0]
207 def AcquireInstance():
208 """Returns an instance which isn't in use.
211 # Filter out unwanted instances
212 tmp_flt = lambda inst: not inst.get("_used", False)
213 instances = filter(tmp_flt, cfg["instances"])
216 if len(instances) == 0:
217 raise qa_error.OutOfInstancesError("No instances left")
224 def ReleaseInstance(inst):
225 inst["_used"] = False
228 def AcquireNode(exclude=None):
229 """Returns the least used node.
232 master = GetMasterNode()
234 # Filter out unwanted nodes
235 # TODO: Maybe combine filters
237 nodes = cfg["nodes"][:]
238 elif isinstance(exclude, (list, tuple)):
239 nodes = filter(lambda node: node not in exclude, cfg["nodes"])
241 nodes = filter(lambda node: node != exclude, cfg["nodes"])
243 tmp_flt = lambda node: node.get("_added", False) or node == master
244 nodes = filter(tmp_flt, nodes)
248 raise qa_error.OutOfNodesError("No nodes left")
250 # Get node with least number of uses
252 result = cmp(a.get("_count", 0), b.get("_count", 0))
254 result = cmp(a["primary"], b["primary"])
257 nodes.sort(cmp=compare)
260 node["_count"] = node.get("_count", 0) + 1
264 def ReleaseNode(node):
265 node["_count"] = node.get("_count", 0) - 1