Statistics
| Branch: | Tag: | Revision:

root / qa / qa_config.py @ f346a7d9

History | View | Annotate | Download (5.1 kB)

1
#
2
#
3

    
4
# Copyright (C) 2007, 2011, 2012 Google Inc.
5
#
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.
10
#
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.
15
#
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
19
# 02110-1301, USA.
20

    
21

    
22
"""QA configuration.
23

24
"""
25

    
26
import os
27

    
28
from ganeti import utils
29
from ganeti import serializer
30
from ganeti import compat
31

    
32
import qa_error
33

    
34

    
35
_INSTANCE_CHECK_KEY = "instance-check"
36

    
37

    
38
cfg = None
39
options = None
40

    
41

    
42
def Load(path):
43
  """Loads the passed configuration file.
44

45
  """
46
  global cfg # pylint: disable=W0603
47

    
48
  cfg = serializer.LoadJson(utils.ReadFile(path))
49

    
50
  Validate()
51

    
52

    
53
def Validate():
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")
61

    
62
  check = GetInstanceCheckScript()
63
  if check:
64
    try:
65
      os.stat(check)
66
    except EnvironmentError, err:
67
      raise qa_error.Error("Can't find instance check script '%s': %s" %
68
                           (check, err))
69

    
70

    
71
def get(name, default=None):
72
  return cfg.get(name, default)
73

    
74

    
75
class Either:
76
  def __init__(self, tests):
77
    """Initializes this class.
78

79
    @type tests: list or string
80
    @param tests: List of test names
81
    @see: L{TestEnabled} for details
82

83
    """
84
    self.tests = tests
85

    
86

    
87
def _MakeSequence(value):
88
  """Make sequence of single argument.
89

90
  If the single argument is not already a list or tuple, a list with the
91
  argument as a single item is returned.
92

93
  """
94
  if isinstance(value, (list, tuple)):
95
    return value
96
  else:
97
    return [value]
98

    
99

    
100
def _TestEnabledInner(check_fn, names, fn):
101
  """Evaluate test conditions.
102

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)
107
  @type fn: callable
108
  @param fn: Aggregation function
109
  @rtype: bool
110
  @return: Whether test is enabled
111

112
  """
113
  names = _MakeSequence(names)
114

    
115
  result = []
116

    
117
  for name in 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)
122
    else:
123
      value = check_fn(name)
124

    
125
    result.append(value)
126

    
127
  return fn(result)
128

    
129

    
130
def TestEnabled(tests, _cfg=None):
131
  """Returns True if the given tests are enabled.
132

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
135

136
  """
137
  if _cfg is None:
138
    _cfg = cfg
139

    
140
  # Get settings for all tests
141
  cfg_tests = _cfg.get("tests", {})
142

    
143
  # Get default setting
144
  default = cfg_tests.get("default", True)
145

    
146
  return _TestEnabledInner(lambda name: cfg_tests.get(name, default),
147
                           tests, compat.all)
148

    
149

    
150
def GetInstanceCheckScript():
151
  """Returns path to instance check script or C{None}.
152

153
  """
154
  return cfg.get(_INSTANCE_CHECK_KEY, None)
155

    
156

    
157
def GetInstanceNicMac(inst, default=None):
158
  """Returns MAC address for instance's network interface.
159

160
  """
161
  return inst.get("nic.mac/0", default)
162

    
163

    
164
def GetMasterNode():
165
  return cfg["nodes"][0]
166

    
167

    
168
def AcquireInstance():
169
  """Returns an instance which isn't in use.
170

171
  """
172
  # Filter out unwanted instances
173
  tmp_flt = lambda inst: not inst.get("_used", False)
174
  instances = filter(tmp_flt, cfg["instances"])
175
  del tmp_flt
176

    
177
  if len(instances) == 0:
178
    raise qa_error.OutOfInstancesError("No instances left")
179

    
180
  inst = instances[0]
181
  inst["_used"] = True
182
  return inst
183

    
184

    
185
def ReleaseInstance(inst):
186
  inst["_used"] = False
187

    
188

    
189
def AcquireNode(exclude=None):
190
  """Returns the least used node.
191

192
  """
193
  master = GetMasterNode()
194

    
195
  # Filter out unwanted nodes
196
  # TODO: Maybe combine filters
197
  if exclude is None:
198
    nodes = cfg["nodes"][:]
199
  elif isinstance(exclude, (list, tuple)):
200
    nodes = filter(lambda node: node not in exclude, cfg["nodes"])
201
  else:
202
    nodes = filter(lambda node: node != exclude, cfg["nodes"])
203

    
204
  tmp_flt = lambda node: node.get("_added", False) or node == master
205
  nodes = filter(tmp_flt, nodes)
206
  del tmp_flt
207

    
208
  if len(nodes) == 0:
209
    raise qa_error.OutOfNodesError("No nodes left")
210

    
211
  # Get node with least number of uses
212
  def compare(a, b):
213
    result = cmp(a.get("_count", 0), b.get("_count", 0))
214
    if result == 0:
215
      result = cmp(a["primary"], b["primary"])
216
    return result
217

    
218
  nodes.sort(cmp=compare)
219

    
220
  node = nodes[0]
221
  node["_count"] = node.get("_count", 0) + 1
222
  return node
223

    
224

    
225
def ReleaseNode(node):
226
  node["_count"] = node.get("_count", 0) - 1