4 # Copyright (C) 2007, 2011 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 utils
29 from ganeti import serializer
30 from ganeti import compat
35 _INSTANCE_CHECK_KEY = "instance-check"
43 """Loads the passed configuration file.
46 global cfg # pylint: disable=W0603
48 cfg = serializer.LoadJson(utils.ReadFile(path))
54 if len(cfg["nodes"]) < 1:
55 raise qa_error.Error("Need at least one node")
56 if len(cfg["instances"]) < 1:
57 raise qa_error.Error("Need at least one instance")
58 if len(cfg["disk"]) != len(cfg["disk-growth"]):
59 raise qa_error.Error("Config options 'disk' and 'disk-growth' must have"
60 " the same number of items")
62 check = GetInstanceCheckScript()
66 except EnvironmentError, err:
67 raise qa_error.Error("Can't find instance check script '%s': %s" %
71 def get(name, default=None):
72 return cfg.get(name, default)
76 def __init__(self, tests):
77 """Initializes this class.
79 @type tests: list or string
80 @param tests: List of test names
81 @see: L{TestEnabled} for details
87 def _MakeSequence(value):
88 """Make sequence of single argument.
90 If the single argument is not already a list or tuple, a list with the
91 argument as a single item is returned.
94 if isinstance(value, (list, tuple)):
100 def _TestEnabledInner(check_fn, names, fn):
101 """Evaluate test conditions.
103 @type check_fn: callable
104 @param check_fn: Callback to check whether a test is enabled
105 @type names: sequence or string
106 @param names: Test name(s)
108 @param fn: Aggregation function
110 @return: Whether test is enabled
113 names = _MakeSequence(names)
118 if isinstance(name, Either):
119 value = _TestEnabledInner(check_fn, name.tests, compat.any)
120 elif isinstance(name, (list, tuple)):
121 value = _TestEnabledInner(check_fn, name, compat.all)
123 value = check_fn(name)
130 def TestEnabled(tests, _cfg=None):
131 """Returns True if the given tests are enabled.
133 @param tests: A single test as a string, or a list of tests to check; can
134 contain L{Either} for OR conditions, AND is default
140 # Get settings for all tests
141 cfg_tests = _cfg.get("tests", {})
143 # Get default setting
144 default = cfg_tests.get("default", True)
146 return _TestEnabledInner(lambda name: cfg_tests.get(name, default),
150 def GetInstanceCheckScript():
151 """Returns path to instance check script or C{None}.
154 return cfg.get(_INSTANCE_CHECK_KEY, None)
158 return cfg["nodes"][0]
161 def AcquireInstance():
162 """Returns an instance which isn't in use.
165 # Filter out unwanted instances
166 tmp_flt = lambda inst: not inst.get("_used", False)
167 instances = filter(tmp_flt, cfg["instances"])
170 if len(instances) == 0:
171 raise qa_error.OutOfInstancesError("No instances left")
178 def ReleaseInstance(inst):
179 inst["_used"] = False
182 def AcquireNode(exclude=None):
183 """Returns the least used node.
186 master = GetMasterNode()
188 # Filter out unwanted nodes
189 # TODO: Maybe combine filters
191 nodes = cfg["nodes"][:]
192 elif isinstance(exclude, (list, tuple)):
193 nodes = filter(lambda node: node not in exclude, cfg["nodes"])
195 nodes = filter(lambda node: node != exclude, cfg["nodes"])
197 tmp_flt = lambda node: node.get("_added", False) or node == master
198 nodes = filter(tmp_flt, nodes)
202 raise qa_error.OutOfNodesError("No nodes left")
204 # Get node with least number of uses
206 result = cmp(a.get("_count", 0), b.get("_count", 0))
208 result = cmp(a["primary"], b["primary"])
211 nodes.sort(cmp=compare)
214 node["_count"] = node.get("_count", 0) + 1
218 def ReleaseNode(node):
219 node["_count"] = node.get("_count", 0) - 1