root / qa / qa_config.py @ 25e9c2ce
History | View | Annotate | Download (23.6 kB)
1 | c68d1f43 | Michael Hanselmann | #
|
---|---|---|---|
2 | c68d1f43 | Michael Hanselmann | #
|
3 | c68d1f43 | Michael Hanselmann | |
4 | 906a0346 | Bernardo Dal Seno | # Copyright (C) 2007, 2011, 2012, 2013 Google Inc.
|
5 | cec9845c | Michael Hanselmann | #
|
6 | cec9845c | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | cec9845c | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | cec9845c | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | cec9845c | Michael Hanselmann | # (at your option) any later version.
|
10 | cec9845c | Michael Hanselmann | #
|
11 | cec9845c | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | cec9845c | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | cec9845c | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | cec9845c | Michael Hanselmann | # General Public License for more details.
|
15 | cec9845c | Michael Hanselmann | #
|
16 | cec9845c | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | cec9845c | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | cec9845c | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | cec9845c | Michael Hanselmann | # 02110-1301, USA.
|
20 | cec9845c | Michael Hanselmann | |
21 | cec9845c | Michael Hanselmann | |
22 | cec9845c | Michael Hanselmann | """QA configuration.
|
23 | cec9845c | Michael Hanselmann |
|
24 | cec9845c | Michael Hanselmann | """
|
25 | cec9845c | Michael Hanselmann | |
26 | c9e05005 | Michael Hanselmann | import os |
27 | cec9845c | Michael Hanselmann | |
28 | e7b6183b | Michael Hanselmann | from ganeti import constants |
29 | a705dc05 | Michael Hanselmann | from ganeti import utils |
30 | a705dc05 | Michael Hanselmann | from ganeti import serializer |
31 | 7d88f255 | Iustin Pop | from ganeti import compat |
32 | 76fda900 | Michael Hanselmann | from ganeti import ht |
33 | cec9845c | Michael Hanselmann | |
34 | cec9845c | Michael Hanselmann | import qa_error |
35 | 7488cf4b | Hrvoje Ribicic | import qa_logging |
36 | cec9845c | Michael Hanselmann | |
37 | cec9845c | Michael Hanselmann | |
38 | c9e05005 | Michael Hanselmann | _INSTANCE_CHECK_KEY = "instance-check"
|
39 | e7b6183b | Michael Hanselmann | _ENABLED_HV_KEY = "enabled-hypervisors"
|
40 | 76fda900 | Michael Hanselmann | _VCLUSTER_MASTER_KEY = "vcluster-master"
|
41 | 76fda900 | Michael Hanselmann | _VCLUSTER_BASEDIR_KEY = "vcluster-basedir"
|
42 | 2dae8d64 | Helga Velroyen | _ENABLED_DISK_TEMPLATES_KEY = "enabled-disk-templates"
|
43 | f9329a6c | Michael Hanselmann | |
44 | a60fc78e | Hrvoje Ribicic | # The constants related to JSON patching (as per RFC6902) that modifies QA's
|
45 | b7630577 | Petr Pudlak | # configuration.
|
46 | c8bf5cf4 | Hrvoje Ribicic | _QA_BASE_PATH = os.path.dirname(__file__) |
47 | c8bf5cf4 | Hrvoje Ribicic | _QA_DEFAULT_PATCH = "qa-patch.json"
|
48 | a60fc78e | Hrvoje Ribicic | _QA_PATCH_DIR = "patch"
|
49 | 7441bb96 | Hrvoje Ribicic | _QA_PATCH_ORDER_FILE = "order"
|
50 | b7630577 | Petr Pudlak | |
51 | 8a96c5a6 | Michael Hanselmann | #: QA configuration (L{_QaConfig})
|
52 | 8a96c5a6 | Michael Hanselmann | _config = None
|
53 | cec9845c | Michael Hanselmann | |
54 | cec9845c | Michael Hanselmann | |
55 | 6a654276 | Michael Hanselmann | class _QaInstance(object): |
56 | 6a654276 | Michael Hanselmann | __slots__ = [ |
57 | 6a654276 | Michael Hanselmann | "name",
|
58 | 6a654276 | Michael Hanselmann | "nicmac",
|
59 | 2176724e | Michael Hanselmann | "_used",
|
60 | 02a5fe0e | Michael Hanselmann | "_disk_template",
|
61 | 6a654276 | Michael Hanselmann | ] |
62 | 6a654276 | Michael Hanselmann | |
63 | 6a654276 | Michael Hanselmann | def __init__(self, name, nicmac): |
64 | 6a654276 | Michael Hanselmann | """Initializes instances of this class.
|
65 | 6a654276 | Michael Hanselmann |
|
66 | 6a654276 | Michael Hanselmann | """
|
67 | 6a654276 | Michael Hanselmann | self.name = name
|
68 | 6a654276 | Michael Hanselmann | self.nicmac = nicmac
|
69 | 2176724e | Michael Hanselmann | self._used = None |
70 | 02a5fe0e | Michael Hanselmann | self._disk_template = None |
71 | 6a654276 | Michael Hanselmann | |
72 | 6a654276 | Michael Hanselmann | @classmethod
|
73 | 6a654276 | Michael Hanselmann | def FromDict(cls, data): |
74 | 6a654276 | Michael Hanselmann | """Creates instance object from JSON dictionary.
|
75 | 6a654276 | Michael Hanselmann |
|
76 | 6a654276 | Michael Hanselmann | """
|
77 | 6a654276 | Michael Hanselmann | nicmac = [] |
78 | 6a654276 | Michael Hanselmann | |
79 | 6a654276 | Michael Hanselmann | macaddr = data.get("nic.mac/0")
|
80 | 6a654276 | Michael Hanselmann | if macaddr:
|
81 | 6a654276 | Michael Hanselmann | nicmac.append(macaddr) |
82 | 6a654276 | Michael Hanselmann | |
83 | 6a654276 | Michael Hanselmann | return cls(name=data["name"], nicmac=nicmac) |
84 | 6a654276 | Michael Hanselmann | |
85 | e80edd3b | Michael Hanselmann | def __repr__(self): |
86 | e80edd3b | Michael Hanselmann | status = [ |
87 | e80edd3b | Michael Hanselmann | "%s.%s" % (self.__class__.__module__, self.__class__.__name__), |
88 | e80edd3b | Michael Hanselmann | "name=%s" % self.name, |
89 | e80edd3b | Michael Hanselmann | "nicmac=%s" % self.nicmac, |
90 | e80edd3b | Michael Hanselmann | "used=%s" % self._used, |
91 | e80edd3b | Michael Hanselmann | "disk_template=%s" % self._disk_template, |
92 | e80edd3b | Michael Hanselmann | ] |
93 | e80edd3b | Michael Hanselmann | |
94 | e80edd3b | Michael Hanselmann | return "<%s at %#x>" % (" ".join(status), id(self)) |
95 | e80edd3b | Michael Hanselmann | |
96 | 2176724e | Michael Hanselmann | def Use(self): |
97 | 2176724e | Michael Hanselmann | """Marks instance as being in use.
|
98 | 2176724e | Michael Hanselmann |
|
99 | 2176724e | Michael Hanselmann | """
|
100 | 2176724e | Michael Hanselmann | assert not self._used |
101 | 2176724e | Michael Hanselmann | assert self._disk_template is None |
102 | 2176724e | Michael Hanselmann | |
103 | 2176724e | Michael Hanselmann | self._used = True |
104 | 2176724e | Michael Hanselmann | |
105 | 6f88e076 | Michael Hanselmann | def Release(self): |
106 | 6f88e076 | Michael Hanselmann | """Releases instance and makes it available again.
|
107 | 6f88e076 | Michael Hanselmann |
|
108 | 6f88e076 | Michael Hanselmann | """
|
109 | 2176724e | Michael Hanselmann | assert self._used, \ |
110 | 6f88e076 | Michael Hanselmann | ("Instance '%s' was never acquired or released more than once" %
|
111 | 6f88e076 | Michael Hanselmann | self.name)
|
112 | 6f88e076 | Michael Hanselmann | |
113 | 2176724e | Michael Hanselmann | self._used = False |
114 | 02a5fe0e | Michael Hanselmann | self._disk_template = None |
115 | 6f88e076 | Michael Hanselmann | |
116 | 6a654276 | Michael Hanselmann | def GetNicMacAddr(self, idx, default): |
117 | 6a654276 | Michael Hanselmann | """Returns MAC address for NIC.
|
118 | 6a654276 | Michael Hanselmann |
|
119 | 6a654276 | Michael Hanselmann | @type idx: int
|
120 | 6a654276 | Michael Hanselmann | @param idx: NIC index
|
121 | 6a654276 | Michael Hanselmann | @param default: Default value
|
122 | 6a654276 | Michael Hanselmann |
|
123 | 6a654276 | Michael Hanselmann | """
|
124 | 6a654276 | Michael Hanselmann | if len(self.nicmac) > idx: |
125 | 6a654276 | Michael Hanselmann | return self.nicmac[idx] |
126 | 6a654276 | Michael Hanselmann | else:
|
127 | 6a654276 | Michael Hanselmann | return default
|
128 | 6a654276 | Michael Hanselmann | |
129 | 02a5fe0e | Michael Hanselmann | def SetDiskTemplate(self, template): |
130 | 02a5fe0e | Michael Hanselmann | """Set the disk template.
|
131 | 02a5fe0e | Michael Hanselmann |
|
132 | 02a5fe0e | Michael Hanselmann | """
|
133 | 02a5fe0e | Michael Hanselmann | assert template in constants.DISK_TEMPLATES |
134 | 02a5fe0e | Michael Hanselmann | |
135 | 02a5fe0e | Michael Hanselmann | self._disk_template = template
|
136 | 02a5fe0e | Michael Hanselmann | |
137 | 02a5fe0e | Michael Hanselmann | @property
|
138 | 2176724e | Michael Hanselmann | def used(self): |
139 | 2176724e | Michael Hanselmann | """Returns boolean denoting whether instance is in use.
|
140 | 2176724e | Michael Hanselmann |
|
141 | 2176724e | Michael Hanselmann | """
|
142 | 2176724e | Michael Hanselmann | return self._used |
143 | 2176724e | Michael Hanselmann | |
144 | 2176724e | Michael Hanselmann | @property
|
145 | 02a5fe0e | Michael Hanselmann | def disk_template(self): |
146 | 02a5fe0e | Michael Hanselmann | """Returns the current disk template.
|
147 | 02a5fe0e | Michael Hanselmann |
|
148 | 02a5fe0e | Michael Hanselmann | """
|
149 | 02a5fe0e | Michael Hanselmann | return self._disk_template |
150 | 02a5fe0e | Michael Hanselmann | |
151 | 6a654276 | Michael Hanselmann | |
152 | dbdb0594 | Michael Hanselmann | class _QaNode(object): |
153 | dbdb0594 | Michael Hanselmann | __slots__ = [ |
154 | dbdb0594 | Michael Hanselmann | "primary",
|
155 | dbdb0594 | Michael Hanselmann | "secondary",
|
156 | dbdb0594 | Michael Hanselmann | "_added",
|
157 | 565cb4bf | Michael Hanselmann | "_use_count",
|
158 | dbdb0594 | Michael Hanselmann | ] |
159 | dbdb0594 | Michael Hanselmann | |
160 | dbdb0594 | Michael Hanselmann | def __init__(self, primary, secondary): |
161 | dbdb0594 | Michael Hanselmann | """Initializes instances of this class.
|
162 | dbdb0594 | Michael Hanselmann |
|
163 | dbdb0594 | Michael Hanselmann | """
|
164 | dbdb0594 | Michael Hanselmann | self.primary = primary
|
165 | dbdb0594 | Michael Hanselmann | self.secondary = secondary
|
166 | dbdb0594 | Michael Hanselmann | self._added = False |
167 | 565cb4bf | Michael Hanselmann | self._use_count = 0 |
168 | dbdb0594 | Michael Hanselmann | |
169 | dbdb0594 | Michael Hanselmann | @classmethod
|
170 | dbdb0594 | Michael Hanselmann | def FromDict(cls, data): |
171 | dbdb0594 | Michael Hanselmann | """Creates node object from JSON dictionary.
|
172 | dbdb0594 | Michael Hanselmann |
|
173 | dbdb0594 | Michael Hanselmann | """
|
174 | dbdb0594 | Michael Hanselmann | return cls(primary=data["primary"], secondary=data.get("secondary")) |
175 | dbdb0594 | Michael Hanselmann | |
176 | e80edd3b | Michael Hanselmann | def __repr__(self): |
177 | e80edd3b | Michael Hanselmann | status = [ |
178 | e80edd3b | Michael Hanselmann | "%s.%s" % (self.__class__.__module__, self.__class__.__name__), |
179 | e80edd3b | Michael Hanselmann | "primary=%s" % self.primary, |
180 | e80edd3b | Michael Hanselmann | "secondary=%s" % self.secondary, |
181 | e80edd3b | Michael Hanselmann | "added=%s" % self._added, |
182 | e80edd3b | Michael Hanselmann | "use_count=%s" % self._use_count, |
183 | e80edd3b | Michael Hanselmann | ] |
184 | e80edd3b | Michael Hanselmann | |
185 | e80edd3b | Michael Hanselmann | return "<%s at %#x>" % (" ".join(status), id(self)) |
186 | e80edd3b | Michael Hanselmann | |
187 | dbdb0594 | Michael Hanselmann | def Use(self): |
188 | dbdb0594 | Michael Hanselmann | """Marks a node as being in use.
|
189 | dbdb0594 | Michael Hanselmann |
|
190 | dbdb0594 | Michael Hanselmann | """
|
191 | 565cb4bf | Michael Hanselmann | assert self._use_count >= 0 |
192 | dbdb0594 | Michael Hanselmann | |
193 | 565cb4bf | Michael Hanselmann | self._use_count += 1 |
194 | dbdb0594 | Michael Hanselmann | |
195 | dbdb0594 | Michael Hanselmann | return self |
196 | dbdb0594 | Michael Hanselmann | |
197 | 565cb4bf | Michael Hanselmann | def Release(self): |
198 | 565cb4bf | Michael Hanselmann | """Release a node (opposite of L{Use}).
|
199 | 565cb4bf | Michael Hanselmann |
|
200 | 565cb4bf | Michael Hanselmann | """
|
201 | 565cb4bf | Michael Hanselmann | assert self.use_count > 0 |
202 | 565cb4bf | Michael Hanselmann | |
203 | 565cb4bf | Michael Hanselmann | self._use_count -= 1 |
204 | 565cb4bf | Michael Hanselmann | |
205 | dbdb0594 | Michael Hanselmann | def MarkAdded(self): |
206 | dbdb0594 | Michael Hanselmann | """Marks node as having been added to a cluster.
|
207 | dbdb0594 | Michael Hanselmann |
|
208 | dbdb0594 | Michael Hanselmann | """
|
209 | dbdb0594 | Michael Hanselmann | assert not self._added |
210 | dbdb0594 | Michael Hanselmann | self._added = True |
211 | dbdb0594 | Michael Hanselmann | |
212 | dbdb0594 | Michael Hanselmann | def MarkRemoved(self): |
213 | dbdb0594 | Michael Hanselmann | """Marks node as having been removed from a cluster.
|
214 | dbdb0594 | Michael Hanselmann |
|
215 | dbdb0594 | Michael Hanselmann | """
|
216 | dbdb0594 | Michael Hanselmann | assert self._added |
217 | dbdb0594 | Michael Hanselmann | self._added = False |
218 | dbdb0594 | Michael Hanselmann | |
219 | dbdb0594 | Michael Hanselmann | @property
|
220 | dbdb0594 | Michael Hanselmann | def added(self): |
221 | dbdb0594 | Michael Hanselmann | """Returns whether a node is part of a cluster.
|
222 | dbdb0594 | Michael Hanselmann |
|
223 | dbdb0594 | Michael Hanselmann | """
|
224 | dbdb0594 | Michael Hanselmann | return self._added |
225 | dbdb0594 | Michael Hanselmann | |
226 | 565cb4bf | Michael Hanselmann | @property
|
227 | 565cb4bf | Michael Hanselmann | def use_count(self): |
228 | 565cb4bf | Michael Hanselmann | """Returns number of current uses (controlled by L{Use} and L{Release}).
|
229 | 565cb4bf | Michael Hanselmann |
|
230 | 565cb4bf | Michael Hanselmann | """
|
231 | 565cb4bf | Michael Hanselmann | return self._use_count |
232 | 565cb4bf | Michael Hanselmann | |
233 | dbdb0594 | Michael Hanselmann | |
234 | 6a654276 | Michael Hanselmann | _RESOURCE_CONVERTER = { |
235 | 6a654276 | Michael Hanselmann | "instances": _QaInstance.FromDict,
|
236 | dbdb0594 | Michael Hanselmann | "nodes": _QaNode.FromDict,
|
237 | 6a654276 | Michael Hanselmann | } |
238 | 6a654276 | Michael Hanselmann | |
239 | 6a654276 | Michael Hanselmann | |
240 | 6a654276 | Michael Hanselmann | def _ConvertResources((key, value)): |
241 | 6a654276 | Michael Hanselmann | """Converts cluster resources in configuration to Python objects.
|
242 | 6a654276 | Michael Hanselmann |
|
243 | 6a654276 | Michael Hanselmann | """
|
244 | 6a654276 | Michael Hanselmann | fn = _RESOURCE_CONVERTER.get(key, None)
|
245 | 6a654276 | Michael Hanselmann | if fn:
|
246 | 6a654276 | Michael Hanselmann | return (key, map(fn, value)) |
247 | 6a654276 | Michael Hanselmann | else:
|
248 | 6a654276 | Michael Hanselmann | return (key, value)
|
249 | 6a654276 | Michael Hanselmann | |
250 | 6a654276 | Michael Hanselmann | |
251 | 8a96c5a6 | Michael Hanselmann | class _QaConfig(object): |
252 | 8a96c5a6 | Michael Hanselmann | def __init__(self, data): |
253 | 8a96c5a6 | Michael Hanselmann | """Initializes instances of this class.
|
254 | cec9845c | Michael Hanselmann |
|
255 | 8a96c5a6 | Michael Hanselmann | """
|
256 | 8a96c5a6 | Michael Hanselmann | self._data = data
|
257 | 8a96c5a6 | Michael Hanselmann | |
258 | a08e181f | Michael Hanselmann | #: Cluster-wide run-time value of the exclusive storage flag
|
259 | a08e181f | Michael Hanselmann | self._exclusive_storage = None |
260 | a08e181f | Michael Hanselmann | |
261 | c8bf5cf4 | Hrvoje Ribicic | @staticmethod
|
262 | c8bf5cf4 | Hrvoje Ribicic | def LoadPatch(patch_dict, rel_path): |
263 | c8bf5cf4 | Hrvoje Ribicic | """ Loads a single patch.
|
264 | c8bf5cf4 | Hrvoje Ribicic |
|
265 | c8bf5cf4 | Hrvoje Ribicic | @type patch_dict: dict of string to dict
|
266 | c8bf5cf4 | Hrvoje Ribicic | @param patch_dict: A dictionary storing patches by relative path.
|
267 | c8bf5cf4 | Hrvoje Ribicic | @type rel_path: string
|
268 | c8bf5cf4 | Hrvoje Ribicic | @param rel_path: The relative path to the patch, might or might not exist.
|
269 | c8bf5cf4 | Hrvoje Ribicic |
|
270 | c8bf5cf4 | Hrvoje Ribicic | """
|
271 | c8bf5cf4 | Hrvoje Ribicic | try:
|
272 | c8bf5cf4 | Hrvoje Ribicic | full_path = os.path.join(_QA_BASE_PATH, rel_path) |
273 | c8bf5cf4 | Hrvoje Ribicic | patch = serializer.LoadJson(utils.ReadFile(full_path)) |
274 | 7488cf4b | Hrvoje Ribicic | patch_dict[rel_path] = patch |
275 | c8bf5cf4 | Hrvoje Ribicic | except IOError: |
276 | c8bf5cf4 | Hrvoje Ribicic | pass
|
277 | c8bf5cf4 | Hrvoje Ribicic | |
278 | c8bf5cf4 | Hrvoje Ribicic | @staticmethod
|
279 | c8bf5cf4 | Hrvoje Ribicic | def LoadPatches(): |
280 | 7488cf4b | Hrvoje Ribicic | """ Finds and loads all patches supported by the QA.
|
281 | c8bf5cf4 | Hrvoje Ribicic |
|
282 | c8bf5cf4 | Hrvoje Ribicic | @rtype: dict of string to dict
|
283 | 7488cf4b | Hrvoje Ribicic | @return: A dictionary of relative path to patch content.
|
284 | c8bf5cf4 | Hrvoje Ribicic |
|
285 | c8bf5cf4 | Hrvoje Ribicic | """
|
286 | c8bf5cf4 | Hrvoje Ribicic | patches = {} |
287 | c8bf5cf4 | Hrvoje Ribicic | _QaConfig.LoadPatch(patches, _QA_DEFAULT_PATCH) |
288 | a60fc78e | Hrvoje Ribicic | patch_dir_path = os.path.join(_QA_BASE_PATH, _QA_PATCH_DIR) |
289 | a60fc78e | Hrvoje Ribicic | if os.path.exists(patch_dir_path):
|
290 | a60fc78e | Hrvoje Ribicic | for filename in os.listdir(patch_dir_path): |
291 | a60fc78e | Hrvoje Ribicic | if filename.endswith(".json"): |
292 | a60fc78e | Hrvoje Ribicic | _QaConfig.LoadPatch(patches, os.path.join(_QA_PATCH_DIR, filename)) |
293 | c8bf5cf4 | Hrvoje Ribicic | return patches
|
294 | c8bf5cf4 | Hrvoje Ribicic | |
295 | a60fc78e | Hrvoje Ribicic | @staticmethod
|
296 | 7488cf4b | Hrvoje Ribicic | def ApplyPatch(data, patch_module, patches, patch_path): |
297 | 7488cf4b | Hrvoje Ribicic | """Applies a single patch.
|
298 | 7488cf4b | Hrvoje Ribicic |
|
299 | 7488cf4b | Hrvoje Ribicic | @type data: dict (deserialized json)
|
300 | 7488cf4b | Hrvoje Ribicic | @param data: The QA configuration
|
301 | 7488cf4b | Hrvoje Ribicic | @type patch_module: module
|
302 | 7488cf4b | Hrvoje Ribicic | @param patch_module: The json patch module, loaded dynamically
|
303 | 7488cf4b | Hrvoje Ribicic | @type patches: dict of string to dict
|
304 | 7488cf4b | Hrvoje Ribicic | @param patches: The dictionary of patch path to content
|
305 | 7488cf4b | Hrvoje Ribicic | @type patch_path: string
|
306 | 7488cf4b | Hrvoje Ribicic | @param patch_path: The path to the patch, relative to the QA directory
|
307 | 7488cf4b | Hrvoje Ribicic |
|
308 | 7488cf4b | Hrvoje Ribicic | @return: The modified configuration data.
|
309 | 7488cf4b | Hrvoje Ribicic |
|
310 | 7488cf4b | Hrvoje Ribicic | """
|
311 | 7488cf4b | Hrvoje Ribicic | patch_content = patches[patch_path] |
312 | 7488cf4b | Hrvoje Ribicic | print qa_logging.FormatInfo("Applying patch %s" % patch_path) |
313 | 7488cf4b | Hrvoje Ribicic | if not patch_content and patch_path != _QA_DEFAULT_PATCH: |
314 | 7488cf4b | Hrvoje Ribicic | print qa_logging.FormatWarning("The patch %s added by the user is empty" % |
315 | 7488cf4b | Hrvoje Ribicic | patch_path) |
316 | 7488cf4b | Hrvoje Ribicic | data = patch_module.apply_patch(data, patch_content) |
317 | 7488cf4b | Hrvoje Ribicic | |
318 | 7488cf4b | Hrvoje Ribicic | @staticmethod
|
319 | a60fc78e | Hrvoje Ribicic | def ApplyPatches(data, patch_module, patches): |
320 | a60fc78e | Hrvoje Ribicic | """Applies any patches present, and returns the modified QA configuration.
|
321 | a60fc78e | Hrvoje Ribicic |
|
322 | 7441bb96 | Hrvoje Ribicic | First, patches from the patch directory are applied. They are ordered
|
323 | 7441bb96 | Hrvoje Ribicic | alphabetically, unless there is an ``order`` file present - any patches
|
324 | 7441bb96 | Hrvoje Ribicic | listed within are applied in that order, and any remaining ones in
|
325 | 7441bb96 | Hrvoje Ribicic | alphabetical order again. Finally, the default patch residing in the
|
326 | 7441bb96 | Hrvoje Ribicic | top-level QA directory is applied.
|
327 | a60fc78e | Hrvoje Ribicic |
|
328 | a60fc78e | Hrvoje Ribicic | @type data: dict (deserialized json)
|
329 | a60fc78e | Hrvoje Ribicic | @param data: The QA configuration
|
330 | a60fc78e | Hrvoje Ribicic | @type patch_module: module
|
331 | a60fc78e | Hrvoje Ribicic | @param patch_module: The json patch module, loaded dynamically
|
332 | a60fc78e | Hrvoje Ribicic | @type patches: dict of string to dict
|
333 | a60fc78e | Hrvoje Ribicic | @param patches: The dictionary of patch path to content
|
334 | a60fc78e | Hrvoje Ribicic |
|
335 | a60fc78e | Hrvoje Ribicic | @return: The modified configuration data.
|
336 | a60fc78e | Hrvoje Ribicic |
|
337 | a60fc78e | Hrvoje Ribicic | """
|
338 | 7441bb96 | Hrvoje Ribicic | ordered_patches = [] |
339 | 7441bb96 | Hrvoje Ribicic | order_path = os.path.join(_QA_BASE_PATH, _QA_PATCH_DIR, |
340 | 7441bb96 | Hrvoje Ribicic | _QA_PATCH_ORDER_FILE) |
341 | 7441bb96 | Hrvoje Ribicic | if os.path.exists(order_path):
|
342 | 7441bb96 | Hrvoje Ribicic | order_file = open(order_path, 'r') |
343 | 7441bb96 | Hrvoje Ribicic | ordered_patches = order_file.read().splitlines() |
344 | 7441bb96 | Hrvoje Ribicic | # Removes empty lines
|
345 | 7441bb96 | Hrvoje Ribicic | ordered_patches = filter(None, ordered_patches) |
346 | 7441bb96 | Hrvoje Ribicic | |
347 | 7441bb96 | Hrvoje Ribicic | # Add the patch dir
|
348 | 7441bb96 | Hrvoje Ribicic | ordered_patches = map(lambda x: os.path.join(_QA_PATCH_DIR, x), |
349 | 7441bb96 | Hrvoje Ribicic | ordered_patches) |
350 | 7441bb96 | Hrvoje Ribicic | |
351 | 7441bb96 | Hrvoje Ribicic | # First the ordered patches
|
352 | 7441bb96 | Hrvoje Ribicic | for patch in ordered_patches: |
353 | 7441bb96 | Hrvoje Ribicic | if patch not in patches: |
354 | 7441bb96 | Hrvoje Ribicic | raise qa_error.Error("Patch %s specified in the ordering file does not " |
355 | 7441bb96 | Hrvoje Ribicic | "exist" % patch)
|
356 | 7488cf4b | Hrvoje Ribicic | _QaConfig.ApplyPatch(data, patch_module, patches, patch) |
357 | 7441bb96 | Hrvoje Ribicic | |
358 | 7441bb96 | Hrvoje Ribicic | # Then the other non-default ones
|
359 | a60fc78e | Hrvoje Ribicic | for patch in sorted(patches): |
360 | 7441bb96 | Hrvoje Ribicic | if patch != _QA_DEFAULT_PATCH and patch not in ordered_patches: |
361 | 7488cf4b | Hrvoje Ribicic | _QaConfig.ApplyPatch(data, patch_module, patches, patch) |
362 | a60fc78e | Hrvoje Ribicic | |
363 | 7441bb96 | Hrvoje Ribicic | # Finally the default one
|
364 | a60fc78e | Hrvoje Ribicic | if _QA_DEFAULT_PATCH in patches: |
365 | 7488cf4b | Hrvoje Ribicic | _QaConfig.ApplyPatch(data, patch_module, patches, _QA_DEFAULT_PATCH) |
366 | a60fc78e | Hrvoje Ribicic | |
367 | a60fc78e | Hrvoje Ribicic | return data
|
368 | a60fc78e | Hrvoje Ribicic | |
369 | 8a96c5a6 | Michael Hanselmann | @classmethod
|
370 | 8a96c5a6 | Michael Hanselmann | def Load(cls, filename): |
371 | 8a96c5a6 | Michael Hanselmann | """Loads a configuration file and produces a configuration object.
|
372 | 8a96c5a6 | Michael Hanselmann |
|
373 | 8a96c5a6 | Michael Hanselmann | @type filename: string
|
374 | 8a96c5a6 | Michael Hanselmann | @param filename: Path to configuration file
|
375 | 8a96c5a6 | Michael Hanselmann | @rtype: L{_QaConfig}
|
376 | 8a96c5a6 | Michael Hanselmann |
|
377 | 8a96c5a6 | Michael Hanselmann | """
|
378 | 8a96c5a6 | Michael Hanselmann | data = serializer.LoadJson(utils.ReadFile(filename)) |
379 | 8a96c5a6 | Michael Hanselmann | |
380 | b7630577 | Petr Pudlak | # Patch the document using JSON Patch (RFC6902) in file _PATCH_JSON, if
|
381 | b7630577 | Petr Pudlak | # available
|
382 | b7630577 | Petr Pudlak | try:
|
383 | c8bf5cf4 | Hrvoje Ribicic | patches = _QaConfig.LoadPatches() |
384 | 7488cf4b | Hrvoje Ribicic | # Try to use the module only if there is a non-empty patch present
|
385 | 7488cf4b | Hrvoje Ribicic | if any(patches.values()): |
386 | b7630577 | Petr Pudlak | mod = __import__("jsonpatch", fromlist=[]) |
387 | a60fc78e | Hrvoje Ribicic | data = _QaConfig.ApplyPatches(data, mod, patches) |
388 | b7630577 | Petr Pudlak | except IOError: |
389 | b7630577 | Petr Pudlak | pass
|
390 | b7630577 | Petr Pudlak | except ImportError: |
391 | c8bf5cf4 | Hrvoje Ribicic | raise qa_error.Error("For the QA JSON patching feature to work, you " |
392 | c8bf5cf4 | Hrvoje Ribicic | "need to install Python modules 'jsonpatch' and "
|
393 | c8bf5cf4 | Hrvoje Ribicic | "'jsonpointer'.")
|
394 | b7630577 | Petr Pudlak | |
395 | 6a654276 | Michael Hanselmann | result = cls(dict(map(_ConvertResources, |
396 | 6a654276 | Michael Hanselmann | data.items()))) # pylint: disable=E1103
|
397 | 8a96c5a6 | Michael Hanselmann | result.Validate() |
398 | 8a96c5a6 | Michael Hanselmann | |
399 | 8a96c5a6 | Michael Hanselmann | return result
|
400 | 8a96c5a6 | Michael Hanselmann | |
401 | 8a96c5a6 | Michael Hanselmann | def Validate(self): |
402 | 8a96c5a6 | Michael Hanselmann | """Validates loaded configuration data.
|
403 | 8a96c5a6 | Michael Hanselmann |
|
404 | 8a96c5a6 | Michael Hanselmann | """
|
405 | 47aa6ec9 | Michael Hanselmann | if not self.get("name"): |
406 | 47aa6ec9 | Michael Hanselmann | raise qa_error.Error("Cluster name is required") |
407 | 47aa6ec9 | Michael Hanselmann | |
408 | 8a96c5a6 | Michael Hanselmann | if not self.get("nodes"): |
409 | 8a96c5a6 | Michael Hanselmann | raise qa_error.Error("Need at least one node") |
410 | 8a96c5a6 | Michael Hanselmann | |
411 | 8a96c5a6 | Michael Hanselmann | if not self.get("instances"): |
412 | 8a96c5a6 | Michael Hanselmann | raise qa_error.Error("Need at least one instance") |
413 | 8a96c5a6 | Michael Hanselmann | |
414 | 090128b6 | Christos Stavrakakis | disks = self.GetDiskOptions()
|
415 | 090128b6 | Christos Stavrakakis | if disks is None: |
416 | 090128b6 | Christos Stavrakakis | raise qa_error.Error("Config option 'disks' must exist") |
417 | 090128b6 | Christos Stavrakakis | else:
|
418 | 090128b6 | Christos Stavrakakis | for d in disks: |
419 | 090128b6 | Christos Stavrakakis | if d.get("size") is None or d.get("growth") is None: |
420 | 090128b6 | Christos Stavrakakis | raise qa_error.Error("Config options `size` and `growth` must exist" |
421 | 090128b6 | Christos Stavrakakis | " for all `disks` items")
|
422 | 8a96c5a6 | Michael Hanselmann | check = self.GetInstanceCheckScript()
|
423 | 8a96c5a6 | Michael Hanselmann | if check:
|
424 | 8a96c5a6 | Michael Hanselmann | try:
|
425 | 8a96c5a6 | Michael Hanselmann | os.stat(check) |
426 | 8a96c5a6 | Michael Hanselmann | except EnvironmentError, err: |
427 | 8a96c5a6 | Michael Hanselmann | raise qa_error.Error("Can't find instance check script '%s': %s" % |
428 | 8a96c5a6 | Michael Hanselmann | (check, err)) |
429 | 8a96c5a6 | Michael Hanselmann | |
430 | 8a96c5a6 | Michael Hanselmann | enabled_hv = frozenset(self.GetEnabledHypervisors()) |
431 | 8a96c5a6 | Michael Hanselmann | if not enabled_hv: |
432 | 8a96c5a6 | Michael Hanselmann | raise qa_error.Error("No hypervisor is enabled") |
433 | 8a96c5a6 | Michael Hanselmann | |
434 | 8a96c5a6 | Michael Hanselmann | difference = enabled_hv - constants.HYPER_TYPES |
435 | 8a96c5a6 | Michael Hanselmann | if difference:
|
436 | 8a96c5a6 | Michael Hanselmann | raise qa_error.Error("Unknown hypervisor(s) enabled: %s" % |
437 | 8a96c5a6 | Michael Hanselmann | utils.CommaJoin(difference)) |
438 | 8a96c5a6 | Michael Hanselmann | |
439 | 76fda900 | Michael Hanselmann | (vc_master, vc_basedir) = self.GetVclusterSettings()
|
440 | 76fda900 | Michael Hanselmann | if bool(vc_master) != bool(vc_basedir): |
441 | 76fda900 | Michael Hanselmann | raise qa_error.Error("All or none of the config options '%s' and '%s'" |
442 | 76fda900 | Michael Hanselmann | " must be set" %
|
443 | 76fda900 | Michael Hanselmann | (_VCLUSTER_MASTER_KEY, _VCLUSTER_BASEDIR_KEY)) |
444 | 76fda900 | Michael Hanselmann | |
445 | 76fda900 | Michael Hanselmann | if vc_basedir and not utils.IsNormAbsPath(vc_basedir): |
446 | 76fda900 | Michael Hanselmann | raise qa_error.Error("Path given in option '%s' must be absolute and" |
447 | 76fda900 | Michael Hanselmann | " normalized" % _VCLUSTER_BASEDIR_KEY)
|
448 | 76fda900 | Michael Hanselmann | |
449 | 8a96c5a6 | Michael Hanselmann | def __getitem__(self, name): |
450 | 8a96c5a6 | Michael Hanselmann | """Returns configuration value.
|
451 | 8a96c5a6 | Michael Hanselmann |
|
452 | 8a96c5a6 | Michael Hanselmann | @type name: string
|
453 | 8a96c5a6 | Michael Hanselmann | @param name: Name of configuration entry
|
454 | 8a96c5a6 | Michael Hanselmann |
|
455 | 8a96c5a6 | Michael Hanselmann | """
|
456 | 8a96c5a6 | Michael Hanselmann | return self._data[name] |
457 | 8a96c5a6 | Michael Hanselmann | |
458 | aac832d2 | Michele Tartara | def __setitem__(self, key, value): |
459 | aac832d2 | Michele Tartara | """Sets a configuration value.
|
460 | aac832d2 | Michele Tartara |
|
461 | aac832d2 | Michele Tartara | """
|
462 | aac832d2 | Michele Tartara | self._data[key] = value
|
463 | aac832d2 | Michele Tartara | |
464 | aac832d2 | Michele Tartara | def __delitem__(self, key): |
465 | aac832d2 | Michele Tartara | """Deletes a value from the configuration.
|
466 | aac832d2 | Michele Tartara |
|
467 | aac832d2 | Michele Tartara | """
|
468 | aac832d2 | Michele Tartara | del(self._data[key]) |
469 | aac832d2 | Michele Tartara | |
470 | aac832d2 | Michele Tartara | def __len__(self): |
471 | aac832d2 | Michele Tartara | """Return the number of configuration items.
|
472 | aac832d2 | Michele Tartara |
|
473 | aac832d2 | Michele Tartara | """
|
474 | aac832d2 | Michele Tartara | return len(self._data) |
475 | aac832d2 | Michele Tartara | |
476 | 8a96c5a6 | Michael Hanselmann | def get(self, name, default=None): |
477 | 8a96c5a6 | Michael Hanselmann | """Returns configuration value.
|
478 | 8a96c5a6 | Michael Hanselmann |
|
479 | 8a96c5a6 | Michael Hanselmann | @type name: string
|
480 | 8a96c5a6 | Michael Hanselmann | @param name: Name of configuration entry
|
481 | 8a96c5a6 | Michael Hanselmann | @param default: Default value
|
482 | 8a96c5a6 | Michael Hanselmann |
|
483 | 8a96c5a6 | Michael Hanselmann | """
|
484 | 8a96c5a6 | Michael Hanselmann | return self._data.get(name, default) |
485 | cec9845c | Michael Hanselmann | |
486 | 8a96c5a6 | Michael Hanselmann | def GetMasterNode(self): |
487 | 8a96c5a6 | Michael Hanselmann | """Returns the default master node for the cluster.
|
488 | cec9845c | Michael Hanselmann |
|
489 | 8a96c5a6 | Michael Hanselmann | """
|
490 | 8a96c5a6 | Michael Hanselmann | return self["nodes"][0] |
491 | 8a96c5a6 | Michael Hanselmann | |
492 | 8a96c5a6 | Michael Hanselmann | def GetInstanceCheckScript(self): |
493 | 8a96c5a6 | Michael Hanselmann | """Returns path to instance check script or C{None}.
|
494 | 8a96c5a6 | Michael Hanselmann |
|
495 | 8a96c5a6 | Michael Hanselmann | """
|
496 | 8a96c5a6 | Michael Hanselmann | return self._data.get(_INSTANCE_CHECK_KEY, None) |
497 | cec9845c | Michael Hanselmann | |
498 | 8a96c5a6 | Michael Hanselmann | def GetEnabledHypervisors(self): |
499 | 8a96c5a6 | Michael Hanselmann | """Returns list of enabled hypervisors.
|
500 | cec9845c | Michael Hanselmann |
|
501 | 8a96c5a6 | Michael Hanselmann | @rtype: list
|
502 | cec9845c | Michael Hanselmann |
|
503 | 8a96c5a6 | Michael Hanselmann | """
|
504 | dacd8ba4 | Helga Velroyen | return self._GetStringListParameter( |
505 | dacd8ba4 | Helga Velroyen | _ENABLED_HV_KEY, |
506 | dacd8ba4 | Helga Velroyen | [constants.DEFAULT_ENABLED_HYPERVISOR]) |
507 | dacd8ba4 | Helga Velroyen | |
508 | dacd8ba4 | Helga Velroyen | def GetDefaultHypervisor(self): |
509 | dacd8ba4 | Helga Velroyen | """Returns the default hypervisor to be used.
|
510 | dacd8ba4 | Helga Velroyen |
|
511 | dacd8ba4 | Helga Velroyen | """
|
512 | dacd8ba4 | Helga Velroyen | return self.GetEnabledHypervisors()[0] |
513 | dacd8ba4 | Helga Velroyen | |
514 | 2dae8d64 | Helga Velroyen | def GetEnabledDiskTemplates(self): |
515 | 2dae8d64 | Helga Velroyen | """Returns the list of enabled disk templates.
|
516 | dacd8ba4 | Helga Velroyen |
|
517 | dacd8ba4 | Helga Velroyen | @rtype: list
|
518 | dacd8ba4 | Helga Velroyen |
|
519 | dacd8ba4 | Helga Velroyen | """
|
520 | dacd8ba4 | Helga Velroyen | return self._GetStringListParameter( |
521 | 2dae8d64 | Helga Velroyen | _ENABLED_DISK_TEMPLATES_KEY, |
522 | decf86f9 | Helga Velroyen | constants.DEFAULT_ENABLED_DISK_TEMPLATES) |
523 | dacd8ba4 | Helga Velroyen | |
524 | 5949c31c | Helga Velroyen | def GetEnabledStorageTypes(self): |
525 | 5949c31c | Helga Velroyen | """Returns the list of enabled storage types.
|
526 | 5949c31c | Helga Velroyen |
|
527 | 5949c31c | Helga Velroyen | @rtype: list
|
528 | 5949c31c | Helga Velroyen | @returns: the list of storage types enabled for QA
|
529 | 5949c31c | Helga Velroyen |
|
530 | 5949c31c | Helga Velroyen | """
|
531 | 5949c31c | Helga Velroyen | enabled_disk_templates = self.GetEnabledDiskTemplates()
|
532 | 615551b2 | Helga Velroyen | enabled_storage_types = list(
|
533 | 615551b2 | Helga Velroyen | set([constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[dt]
|
534 | 615551b2 | Helga Velroyen | for dt in enabled_disk_templates])) |
535 | 5949c31c | Helga Velroyen | # Storage type 'lvm-pv' cannot be activated via a disk template,
|
536 | 5949c31c | Helga Velroyen | # therefore we add it if 'lvm-vg' is present.
|
537 | 5949c31c | Helga Velroyen | if constants.ST_LVM_VG in enabled_storage_types: |
538 | 5949c31c | Helga Velroyen | enabled_storage_types.append(constants.ST_LVM_PV) |
539 | 5949c31c | Helga Velroyen | return enabled_storage_types
|
540 | 5949c31c | Helga Velroyen | |
541 | 2dae8d64 | Helga Velroyen | def GetDefaultDiskTemplate(self): |
542 | 2dae8d64 | Helga Velroyen | """Returns the default disk template to be used.
|
543 | dacd8ba4 | Helga Velroyen |
|
544 | dacd8ba4 | Helga Velroyen | """
|
545 | 2dae8d64 | Helga Velroyen | return self.GetEnabledDiskTemplates()[0] |
546 | dacd8ba4 | Helga Velroyen | |
547 | dacd8ba4 | Helga Velroyen | def _GetStringListParameter(self, key, default_values): |
548 | dacd8ba4 | Helga Velroyen | """Retrieves a parameter's value that is supposed to be a list of strings.
|
549 | dacd8ba4 | Helga Velroyen |
|
550 | dacd8ba4 | Helga Velroyen | @rtype: list
|
551 | dacd8ba4 | Helga Velroyen |
|
552 | dacd8ba4 | Helga Velroyen | """
|
553 | c9e05005 | Michael Hanselmann | try:
|
554 | dacd8ba4 | Helga Velroyen | value = self._data[key]
|
555 | 8a96c5a6 | Michael Hanselmann | except KeyError: |
556 | dacd8ba4 | Helga Velroyen | return default_values
|
557 | 8a96c5a6 | Michael Hanselmann | else:
|
558 | 8a96c5a6 | Michael Hanselmann | if value is None: |
559 | 8a96c5a6 | Michael Hanselmann | return []
|
560 | 8a96c5a6 | Michael Hanselmann | elif isinstance(value, basestring): |
561 | 8a96c5a6 | Michael Hanselmann | return value.split(",") |
562 | 8a96c5a6 | Michael Hanselmann | else:
|
563 | 8a96c5a6 | Michael Hanselmann | return value
|
564 | 8a96c5a6 | Michael Hanselmann | |
565 | a08e181f | Michael Hanselmann | def SetExclusiveStorage(self, value): |
566 | a08e181f | Michael Hanselmann | """Set the expected value of the C{exclusive_storage} flag for the cluster.
|
567 | a08e181f | Michael Hanselmann |
|
568 | a08e181f | Michael Hanselmann | """
|
569 | a08e181f | Michael Hanselmann | self._exclusive_storage = bool(value) |
570 | a08e181f | Michael Hanselmann | |
571 | a08e181f | Michael Hanselmann | def GetExclusiveStorage(self): |
572 | a08e181f | Michael Hanselmann | """Get the expected value of the C{exclusive_storage} flag for the cluster.
|
573 | a08e181f | Michael Hanselmann |
|
574 | a08e181f | Michael Hanselmann | """
|
575 | a08e181f | Michael Hanselmann | value = self._exclusive_storage
|
576 | a08e181f | Michael Hanselmann | assert value is not None |
577 | a08e181f | Michael Hanselmann | return value
|
578 | a08e181f | Michael Hanselmann | |
579 | a08e181f | Michael Hanselmann | def IsTemplateSupported(self, templ): |
580 | a08e181f | Michael Hanselmann | """Is the given disk template supported by the current configuration?
|
581 | a08e181f | Michael Hanselmann |
|
582 | a08e181f | Michael Hanselmann | """
|
583 | 02cff8aa | Bernardo Dal Seno | enabled = templ in self.GetEnabledDiskTemplates() |
584 | 02cff8aa | Bernardo Dal Seno | return enabled and (not self.GetExclusiveStorage() or |
585 | 02cff8aa | Bernardo Dal Seno | templ in constants.DTS_EXCL_STORAGE)
|
586 | a08e181f | Michael Hanselmann | |
587 | bab4f56a | Helga Velroyen | def IsStorageTypeSupported(self, storage_type): |
588 | bab4f56a | Helga Velroyen | """Is the given storage type supported by the current configuration?
|
589 | bab4f56a | Helga Velroyen |
|
590 | bab4f56a | Helga Velroyen | This is determined by looking if at least one of the disk templates
|
591 | bab4f56a | Helga Velroyen | which is associated with the storage type is enabled in the configuration.
|
592 | bab4f56a | Helga Velroyen |
|
593 | bab4f56a | Helga Velroyen | """
|
594 | bab4f56a | Helga Velroyen | enabled_disk_templates = self.GetEnabledDiskTemplates()
|
595 | bab4f56a | Helga Velroyen | if storage_type == constants.ST_LVM_PV:
|
596 | bab4f56a | Helga Velroyen | disk_templates = utils.GetDiskTemplatesOfStorageType(constants.ST_LVM_VG) |
597 | bab4f56a | Helga Velroyen | else:
|
598 | bab4f56a | Helga Velroyen | disk_templates = utils.GetDiskTemplatesOfStorageType(storage_type) |
599 | bab4f56a | Helga Velroyen | return bool(set(enabled_disk_templates).intersection(set(disk_templates))) |
600 | bab4f56a | Helga Velroyen | |
601 | 345d395d | Bernardo Dal Seno | def AreSpindlesSupported(self): |
602 | 345d395d | Bernardo Dal Seno | """Are spindles supported by the current configuration?
|
603 | 345d395d | Bernardo Dal Seno |
|
604 | 345d395d | Bernardo Dal Seno | """
|
605 | 345d395d | Bernardo Dal Seno | return self.GetExclusiveStorage() |
606 | 345d395d | Bernardo Dal Seno | |
607 | 76fda900 | Michael Hanselmann | def GetVclusterSettings(self): |
608 | 76fda900 | Michael Hanselmann | """Returns settings for virtual cluster.
|
609 | 76fda900 | Michael Hanselmann |
|
610 | 76fda900 | Michael Hanselmann | """
|
611 | 76fda900 | Michael Hanselmann | master = self.get(_VCLUSTER_MASTER_KEY)
|
612 | 76fda900 | Michael Hanselmann | basedir = self.get(_VCLUSTER_BASEDIR_KEY)
|
613 | 76fda900 | Michael Hanselmann | |
614 | 76fda900 | Michael Hanselmann | return (master, basedir)
|
615 | 76fda900 | Michael Hanselmann | |
616 | 090128b6 | Christos Stavrakakis | def GetDiskOptions(self): |
617 | 090128b6 | Christos Stavrakakis | """Return options for the disks of the instances.
|
618 | 090128b6 | Christos Stavrakakis |
|
619 | 090128b6 | Christos Stavrakakis | Get 'disks' parameter from the configuration data. If 'disks' is missing,
|
620 | 090128b6 | Christos Stavrakakis | try to create it from the legacy 'disk' and 'disk-growth' parameters.
|
621 | 090128b6 | Christos Stavrakakis |
|
622 | 090128b6 | Christos Stavrakakis | """
|
623 | 090128b6 | Christos Stavrakakis | try:
|
624 | 090128b6 | Christos Stavrakakis | return self._data["disks"] |
625 | 090128b6 | Christos Stavrakakis | except KeyError: |
626 | 090128b6 | Christos Stavrakakis | pass
|
627 | 090128b6 | Christos Stavrakakis | |
628 | 090128b6 | Christos Stavrakakis | # Legacy interface
|
629 | 090128b6 | Christos Stavrakakis | sizes = self._data.get("disk") |
630 | 090128b6 | Christos Stavrakakis | growths = self._data.get("disk-growth") |
631 | 090128b6 | Christos Stavrakakis | if sizes or growths: |
632 | 090128b6 | Christos Stavrakakis | if (sizes is None or growths is None or len(sizes) != len(growths)): |
633 | 090128b6 | Christos Stavrakakis | raise qa_error.Error("Config options 'disk' and 'disk-growth' must" |
634 | 090128b6 | Christos Stavrakakis | " exist and have the same number of items")
|
635 | 090128b6 | Christos Stavrakakis | disks = [] |
636 | 090128b6 | Christos Stavrakakis | for (size, growth) in zip(sizes, growths): |
637 | 090128b6 | Christos Stavrakakis | disks.append({"size": size, "growth": growth}) |
638 | 090128b6 | Christos Stavrakakis | return disks
|
639 | 090128b6 | Christos Stavrakakis | else:
|
640 | 090128b6 | Christos Stavrakakis | return None |
641 | 090128b6 | Christos Stavrakakis | |
642 | 8a96c5a6 | Michael Hanselmann | |
643 | 8a96c5a6 | Michael Hanselmann | def Load(path): |
644 | 8a96c5a6 | Michael Hanselmann | """Loads the passed configuration file.
|
645 | 8a96c5a6 | Michael Hanselmann |
|
646 | 8a96c5a6 | Michael Hanselmann | """
|
647 | 8a96c5a6 | Michael Hanselmann | global _config # pylint: disable=W0603 |
648 | 8a96c5a6 | Michael Hanselmann | |
649 | 8a96c5a6 | Michael Hanselmann | _config = _QaConfig.Load(path) |
650 | c9e05005 | Michael Hanselmann | |
651 | e7b6183b | Michael Hanselmann | |
652 | 8a96c5a6 | Michael Hanselmann | def GetConfig(): |
653 | 8a96c5a6 | Michael Hanselmann | """Returns the configuration object.
|
654 | 8a96c5a6 | Michael Hanselmann |
|
655 | 8a96c5a6 | Michael Hanselmann | """
|
656 | 8a96c5a6 | Michael Hanselmann | if _config is None: |
657 | 8a96c5a6 | Michael Hanselmann | raise RuntimeError("Configuration not yet loaded") |
658 | 8a96c5a6 | Michael Hanselmann | |
659 | 8a96c5a6 | Michael Hanselmann | return _config
|
660 | e7b6183b | Michael Hanselmann | |
661 | cec9845c | Michael Hanselmann | |
662 | cec9845c | Michael Hanselmann | def get(name, default=None): |
663 | 8a96c5a6 | Michael Hanselmann | """Wrapper for L{_QaConfig.get}.
|
664 | 8a96c5a6 | Michael Hanselmann |
|
665 | 8a96c5a6 | Michael Hanselmann | """
|
666 | 8a96c5a6 | Michael Hanselmann | return GetConfig().get(name, default=default)
|
667 | cec9845c | Michael Hanselmann | |
668 | cec9845c | Michael Hanselmann | |
669 | a0c3e726 | Michael Hanselmann | class Either: |
670 | a0c3e726 | Michael Hanselmann | def __init__(self, tests): |
671 | a0c3e726 | Michael Hanselmann | """Initializes this class.
|
672 | a0c3e726 | Michael Hanselmann |
|
673 | a0c3e726 | Michael Hanselmann | @type tests: list or string
|
674 | a0c3e726 | Michael Hanselmann | @param tests: List of test names
|
675 | a0c3e726 | Michael Hanselmann | @see: L{TestEnabled} for details
|
676 | a0c3e726 | Michael Hanselmann |
|
677 | a0c3e726 | Michael Hanselmann | """
|
678 | a0c3e726 | Michael Hanselmann | self.tests = tests
|
679 | a0c3e726 | Michael Hanselmann | |
680 | a0c3e726 | Michael Hanselmann | |
681 | a0c3e726 | Michael Hanselmann | def _MakeSequence(value): |
682 | a0c3e726 | Michael Hanselmann | """Make sequence of single argument.
|
683 | a0c3e726 | Michael Hanselmann |
|
684 | a0c3e726 | Michael Hanselmann | If the single argument is not already a list or tuple, a list with the
|
685 | a0c3e726 | Michael Hanselmann | argument as a single item is returned.
|
686 | a0c3e726 | Michael Hanselmann |
|
687 | a0c3e726 | Michael Hanselmann | """
|
688 | a0c3e726 | Michael Hanselmann | if isinstance(value, (list, tuple)): |
689 | a0c3e726 | Michael Hanselmann | return value
|
690 | a0c3e726 | Michael Hanselmann | else:
|
691 | a0c3e726 | Michael Hanselmann | return [value]
|
692 | a0c3e726 | Michael Hanselmann | |
693 | a0c3e726 | Michael Hanselmann | |
694 | a0c3e726 | Michael Hanselmann | def _TestEnabledInner(check_fn, names, fn): |
695 | a0c3e726 | Michael Hanselmann | """Evaluate test conditions.
|
696 | a0c3e726 | Michael Hanselmann |
|
697 | a0c3e726 | Michael Hanselmann | @type check_fn: callable
|
698 | a0c3e726 | Michael Hanselmann | @param check_fn: Callback to check whether a test is enabled
|
699 | a0c3e726 | Michael Hanselmann | @type names: sequence or string
|
700 | a0c3e726 | Michael Hanselmann | @param names: Test name(s)
|
701 | a0c3e726 | Michael Hanselmann | @type fn: callable
|
702 | a0c3e726 | Michael Hanselmann | @param fn: Aggregation function
|
703 | a0c3e726 | Michael Hanselmann | @rtype: bool
|
704 | a0c3e726 | Michael Hanselmann | @return: Whether test is enabled
|
705 | a0c3e726 | Michael Hanselmann |
|
706 | a0c3e726 | Michael Hanselmann | """
|
707 | a0c3e726 | Michael Hanselmann | names = _MakeSequence(names) |
708 | a0c3e726 | Michael Hanselmann | |
709 | a0c3e726 | Michael Hanselmann | result = [] |
710 | a0c3e726 | Michael Hanselmann | |
711 | a0c3e726 | Michael Hanselmann | for name in names: |
712 | a0c3e726 | Michael Hanselmann | if isinstance(name, Either): |
713 | a0c3e726 | Michael Hanselmann | value = _TestEnabledInner(check_fn, name.tests, compat.any) |
714 | a0c3e726 | Michael Hanselmann | elif isinstance(name, (list, tuple)): |
715 | a0c3e726 | Michael Hanselmann | value = _TestEnabledInner(check_fn, name, compat.all) |
716 | c072e788 | Michael Hanselmann | elif callable(name): |
717 | c072e788 | Michael Hanselmann | value = name() |
718 | a0c3e726 | Michael Hanselmann | else:
|
719 | a0c3e726 | Michael Hanselmann | value = check_fn(name) |
720 | a0c3e726 | Michael Hanselmann | |
721 | a0c3e726 | Michael Hanselmann | result.append(value) |
722 | a0c3e726 | Michael Hanselmann | |
723 | a0c3e726 | Michael Hanselmann | return fn(result)
|
724 | a0c3e726 | Michael Hanselmann | |
725 | a0c3e726 | Michael Hanselmann | |
726 | a0c3e726 | Michael Hanselmann | def TestEnabled(tests, _cfg=None): |
727 | 7d88f255 | Iustin Pop | """Returns True if the given tests are enabled.
|
728 | 7d88f255 | Iustin Pop |
|
729 | a0c3e726 | Michael Hanselmann | @param tests: A single test as a string, or a list of tests to check; can
|
730 | a0c3e726 | Michael Hanselmann | contain L{Either} for OR conditions, AND is default
|
731 | 1010ec70 | Michael Hanselmann |
|
732 | 1010ec70 | Michael Hanselmann | """
|
733 | a0c3e726 | Michael Hanselmann | if _cfg is None: |
734 | 8a96c5a6 | Michael Hanselmann | cfg = GetConfig() |
735 | 8a96c5a6 | Michael Hanselmann | else:
|
736 | 8a96c5a6 | Michael Hanselmann | cfg = _cfg |
737 | 59a8fe48 | Michael Hanselmann | |
738 | 59a8fe48 | Michael Hanselmann | # Get settings for all tests
|
739 | 8a96c5a6 | Michael Hanselmann | cfg_tests = cfg.get("tests", {})
|
740 | 59a8fe48 | Michael Hanselmann | |
741 | 59a8fe48 | Michael Hanselmann | # Get default setting
|
742 | a0c3e726 | Michael Hanselmann | default = cfg_tests.get("default", True) |
743 | 59a8fe48 | Michael Hanselmann | |
744 | a0c3e726 | Michael Hanselmann | return _TestEnabledInner(lambda name: cfg_tests.get(name, default), |
745 | a0c3e726 | Michael Hanselmann | tests, compat.all) |
746 | cec9845c | Michael Hanselmann | |
747 | cec9845c | Michael Hanselmann | |
748 | 8a96c5a6 | Michael Hanselmann | def GetInstanceCheckScript(*args): |
749 | 8a96c5a6 | Michael Hanselmann | """Wrapper for L{_QaConfig.GetInstanceCheckScript}.
|
750 | c9e05005 | Michael Hanselmann |
|
751 | c9e05005 | Michael Hanselmann | """
|
752 | 8a96c5a6 | Michael Hanselmann | return GetConfig().GetInstanceCheckScript(*args)
|
753 | c9e05005 | Michael Hanselmann | |
754 | e7b6183b | Michael Hanselmann | |
755 | 8a96c5a6 | Michael Hanselmann | def GetEnabledHypervisors(*args): |
756 | 8a96c5a6 | Michael Hanselmann | """Wrapper for L{_QaConfig.GetEnabledHypervisors}.
|
757 | e7b6183b | Michael Hanselmann |
|
758 | e7b6183b | Michael Hanselmann | """
|
759 | 8a96c5a6 | Michael Hanselmann | return GetConfig().GetEnabledHypervisors(*args)
|
760 | e7b6183b | Michael Hanselmann | |
761 | e7b6183b | Michael Hanselmann | |
762 | 8a96c5a6 | Michael Hanselmann | def GetDefaultHypervisor(*args): |
763 | 8a96c5a6 | Michael Hanselmann | """Wrapper for L{_QaConfig.GetDefaultHypervisor}.
|
764 | e7b6183b | Michael Hanselmann |
|
765 | e7b6183b | Michael Hanselmann | """
|
766 | 8a96c5a6 | Michael Hanselmann | return GetConfig().GetDefaultHypervisor(*args)
|
767 | dacd8ba4 | Helga Velroyen | |
768 | dacd8ba4 | Helga Velroyen | |
769 | 2dae8d64 | Helga Velroyen | def GetEnabledDiskTemplates(*args): |
770 | 2dae8d64 | Helga Velroyen | """Wrapper for L{_QaConfig.GetEnabledDiskTemplates}.
|
771 | dacd8ba4 | Helga Velroyen |
|
772 | dacd8ba4 | Helga Velroyen | """
|
773 | 2dae8d64 | Helga Velroyen | return GetConfig().GetEnabledDiskTemplates(*args)
|
774 | dacd8ba4 | Helga Velroyen | |
775 | dacd8ba4 | Helga Velroyen | |
776 | 5949c31c | Helga Velroyen | def GetEnabledStorageTypes(*args): |
777 | 5949c31c | Helga Velroyen | """Wrapper for L{_QaConfig.GetEnabledStorageTypes}.
|
778 | 5949c31c | Helga Velroyen |
|
779 | 5949c31c | Helga Velroyen | """
|
780 | 5949c31c | Helga Velroyen | return GetConfig().GetEnabledStorageTypes(*args)
|
781 | 5949c31c | Helga Velroyen | |
782 | 5949c31c | Helga Velroyen | |
783 | 2dae8d64 | Helga Velroyen | def GetDefaultDiskTemplate(*args): |
784 | 2dae8d64 | Helga Velroyen | """Wrapper for L{_QaConfig.GetDefaultDiskTemplate}.
|
785 | dacd8ba4 | Helga Velroyen |
|
786 | dacd8ba4 | Helga Velroyen | """
|
787 | 2dae8d64 | Helga Velroyen | return GetConfig().GetDefaultDiskTemplate(*args)
|
788 | e7b6183b | Michael Hanselmann | |
789 | e7b6183b | Michael Hanselmann | |
790 | cec9845c | Michael Hanselmann | def GetMasterNode(): |
791 | 8a96c5a6 | Michael Hanselmann | """Wrapper for L{_QaConfig.GetMasterNode}.
|
792 | 8a96c5a6 | Michael Hanselmann |
|
793 | 8a96c5a6 | Michael Hanselmann | """
|
794 | 8a96c5a6 | Michael Hanselmann | return GetConfig().GetMasterNode()
|
795 | cec9845c | Michael Hanselmann | |
796 | cec9845c | Michael Hanselmann | |
797 | 6a654276 | Michael Hanselmann | def AcquireInstance(_cfg=None): |
798 | cec9845c | Michael Hanselmann | """Returns an instance which isn't in use.
|
799 | cec9845c | Michael Hanselmann |
|
800 | cec9845c | Michael Hanselmann | """
|
801 | 6a654276 | Michael Hanselmann | if _cfg is None: |
802 | 6a654276 | Michael Hanselmann | cfg = GetConfig() |
803 | 6a654276 | Michael Hanselmann | else:
|
804 | 6a654276 | Michael Hanselmann | cfg = _cfg |
805 | 6a654276 | Michael Hanselmann | |
806 | cec9845c | Michael Hanselmann | # Filter out unwanted instances
|
807 | 6a654276 | Michael Hanselmann | instances = filter(lambda inst: not inst.used, cfg["instances"]) |
808 | cec9845c | Michael Hanselmann | |
809 | 6a654276 | Michael Hanselmann | if not instances: |
810 | cec9845c | Michael Hanselmann | raise qa_error.OutOfInstancesError("No instances left") |
811 | cec9845c | Michael Hanselmann | |
812 | 2176724e | Michael Hanselmann | instance = instances[0]
|
813 | 2176724e | Michael Hanselmann | instance.Use() |
814 | 6a654276 | Michael Hanselmann | |
815 | 2176724e | Michael Hanselmann | return instance
|
816 | cec9845c | Michael Hanselmann | |
817 | cec9845c | Michael Hanselmann | |
818 | 6a0f22e1 | Bernardo Dal Seno | def SetExclusiveStorage(value): |
819 | a08e181f | Michael Hanselmann | """Wrapper for L{_QaConfig.SetExclusiveStorage}.
|
820 | 6a0f22e1 | Bernardo Dal Seno |
|
821 | 6a0f22e1 | Bernardo Dal Seno | """
|
822 | a08e181f | Michael Hanselmann | return GetConfig().SetExclusiveStorage(value)
|
823 | 6a0f22e1 | Bernardo Dal Seno | |
824 | 6a0f22e1 | Bernardo Dal Seno | |
825 | 6a0f22e1 | Bernardo Dal Seno | def GetExclusiveStorage(): |
826 | a08e181f | Michael Hanselmann | """Wrapper for L{_QaConfig.GetExclusiveStorage}.
|
827 | 6a0f22e1 | Bernardo Dal Seno |
|
828 | 6a0f22e1 | Bernardo Dal Seno | """
|
829 | a08e181f | Michael Hanselmann | return GetConfig().GetExclusiveStorage()
|
830 | 6a0f22e1 | Bernardo Dal Seno | |
831 | 6a0f22e1 | Bernardo Dal Seno | |
832 | 27eba428 | Bernardo Dal Seno | def IsTemplateSupported(templ): |
833 | 02cff8aa | Bernardo Dal Seno | """Wrapper for L{_QaConfig.IsTemplateSupported}.
|
834 | 27eba428 | Bernardo Dal Seno |
|
835 | 27eba428 | Bernardo Dal Seno | """
|
836 | a08e181f | Michael Hanselmann | return GetConfig().IsTemplateSupported(templ)
|
837 | 27eba428 | Bernardo Dal Seno | |
838 | 27eba428 | Bernardo Dal Seno | |
839 | bab4f56a | Helga Velroyen | def IsStorageTypeSupported(storage_type): |
840 | bab4f56a | Helga Velroyen | """Wrapper for L{_QaConfig.IsTemplateSupported}.
|
841 | bab4f56a | Helga Velroyen |
|
842 | bab4f56a | Helga Velroyen | """
|
843 | bab4f56a | Helga Velroyen | return GetConfig().IsStorageTypeSupported(storage_type)
|
844 | bab4f56a | Helga Velroyen | |
845 | bab4f56a | Helga Velroyen | |
846 | 345d395d | Bernardo Dal Seno | def AreSpindlesSupported(): |
847 | 345d395d | Bernardo Dal Seno | """Wrapper for L{_QaConfig.AreSpindlesSupported}.
|
848 | 345d395d | Bernardo Dal Seno |
|
849 | 345d395d | Bernardo Dal Seno | """
|
850 | 345d395d | Bernardo Dal Seno | return GetConfig().AreSpindlesSupported()
|
851 | 345d395d | Bernardo Dal Seno | |
852 | 345d395d | Bernardo Dal Seno | |
853 | 41be279f | Michael Hanselmann | def _NodeSortKey(node): |
854 | 41be279f | Michael Hanselmann | """Returns sort key for a node.
|
855 | 41be279f | Michael Hanselmann |
|
856 | 41be279f | Michael Hanselmann | @type node: L{_QaNode}
|
857 | 41be279f | Michael Hanselmann |
|
858 | 41be279f | Michael Hanselmann | """
|
859 | 41be279f | Michael Hanselmann | return (node.use_count, utils.NiceSortKey(node.primary))
|
860 | 41be279f | Michael Hanselmann | |
861 | 41be279f | Michael Hanselmann | |
862 | dbdb0594 | Michael Hanselmann | def AcquireNode(exclude=None, _cfg=None): |
863 | cec9845c | Michael Hanselmann | """Returns the least used node.
|
864 | cec9845c | Michael Hanselmann |
|
865 | cec9845c | Michael Hanselmann | """
|
866 | dbdb0594 | Michael Hanselmann | if _cfg is None: |
867 | dbdb0594 | Michael Hanselmann | cfg = GetConfig() |
868 | dbdb0594 | Michael Hanselmann | else:
|
869 | dbdb0594 | Michael Hanselmann | cfg = _cfg |
870 | dbdb0594 | Michael Hanselmann | |
871 | dbdb0594 | Michael Hanselmann | master = cfg.GetMasterNode() |
872 | cec9845c | Michael Hanselmann | |
873 | cec9845c | Michael Hanselmann | # Filter out unwanted nodes
|
874 | cec9845c | Michael Hanselmann | # TODO: Maybe combine filters
|
875 | cec9845c | Michael Hanselmann | if exclude is None: |
876 | d0c8c01d | Iustin Pop | nodes = cfg["nodes"][:]
|
877 | 4b62db14 | Michael Hanselmann | elif isinstance(exclude, (list, tuple)): |
878 | d0c8c01d | Iustin Pop | nodes = filter(lambda node: node not in exclude, cfg["nodes"]) |
879 | cec9845c | Michael Hanselmann | else:
|
880 | d0c8c01d | Iustin Pop | nodes = filter(lambda node: node != exclude, cfg["nodes"]) |
881 | cec9845c | Michael Hanselmann | |
882 | dbdb0594 | Michael Hanselmann | nodes = filter(lambda node: node.added or node == master, nodes) |
883 | cec9845c | Michael Hanselmann | |
884 | dbdb0594 | Michael Hanselmann | if not nodes: |
885 | cec9845c | Michael Hanselmann | raise qa_error.OutOfNodesError("No nodes left") |
886 | cec9845c | Michael Hanselmann | |
887 | 41be279f | Michael Hanselmann | # Return node with least number of uses
|
888 | 41be279f | Michael Hanselmann | return sorted(nodes, key=_NodeSortKey)[0].Use() |
889 | cec9845c | Michael Hanselmann | |
890 | cec9845c | Michael Hanselmann | |
891 | 7d4f1b45 | Bernardo Dal Seno | def AcquireManyNodes(num, exclude=None): |
892 | 7d4f1b45 | Bernardo Dal Seno | """Return the least used nodes.
|
893 | 7d4f1b45 | Bernardo Dal Seno |
|
894 | 7d4f1b45 | Bernardo Dal Seno | @type num: int
|
895 | 7d4f1b45 | Bernardo Dal Seno | @param num: Number of nodes; can be 0.
|
896 | 7d4f1b45 | Bernardo Dal Seno | @type exclude: list of nodes or C{None}
|
897 | 7d4f1b45 | Bernardo Dal Seno | @param exclude: nodes to be excluded from the choice
|
898 | 7d4f1b45 | Bernardo Dal Seno | @rtype: list of nodes
|
899 | 7d4f1b45 | Bernardo Dal Seno | @return: C{num} different nodes
|
900 | 7d4f1b45 | Bernardo Dal Seno |
|
901 | 7d4f1b45 | Bernardo Dal Seno | """
|
902 | 7d4f1b45 | Bernardo Dal Seno | nodes = [] |
903 | 7d4f1b45 | Bernardo Dal Seno | if exclude is None: |
904 | 7d4f1b45 | Bernardo Dal Seno | exclude = [] |
905 | 7d4f1b45 | Bernardo Dal Seno | elif isinstance(exclude, (list, tuple)): |
906 | 7d4f1b45 | Bernardo Dal Seno | # Don't modify the incoming argument
|
907 | 7d4f1b45 | Bernardo Dal Seno | exclude = list(exclude)
|
908 | 7d4f1b45 | Bernardo Dal Seno | else:
|
909 | 7d4f1b45 | Bernardo Dal Seno | exclude = [exclude] |
910 | 7d4f1b45 | Bernardo Dal Seno | |
911 | 7d4f1b45 | Bernardo Dal Seno | try:
|
912 | 7d4f1b45 | Bernardo Dal Seno | for _ in range(0, num): |
913 | 7d4f1b45 | Bernardo Dal Seno | n = AcquireNode(exclude=exclude) |
914 | 7d4f1b45 | Bernardo Dal Seno | nodes.append(n) |
915 | 7d4f1b45 | Bernardo Dal Seno | exclude.append(n) |
916 | 7d4f1b45 | Bernardo Dal Seno | except qa_error.OutOfNodesError:
|
917 | 7d4f1b45 | Bernardo Dal Seno | ReleaseManyNodes(nodes) |
918 | 7d4f1b45 | Bernardo Dal Seno | raise
|
919 | 7d4f1b45 | Bernardo Dal Seno | return nodes
|
920 | 7d4f1b45 | Bernardo Dal Seno | |
921 | 7d4f1b45 | Bernardo Dal Seno | |
922 | 7d4f1b45 | Bernardo Dal Seno | def ReleaseManyNodes(nodes): |
923 | 565cb4bf | Michael Hanselmann | for node in nodes: |
924 | 565cb4bf | Michael Hanselmann | node.Release() |
925 | 76fda900 | Michael Hanselmann | |
926 | 76fda900 | Michael Hanselmann | |
927 | 76fda900 | Michael Hanselmann | def GetVclusterSettings(): |
928 | 76fda900 | Michael Hanselmann | """Wrapper for L{_QaConfig.GetVclusterSettings}.
|
929 | 76fda900 | Michael Hanselmann |
|
930 | 76fda900 | Michael Hanselmann | """
|
931 | 76fda900 | Michael Hanselmann | return GetConfig().GetVclusterSettings()
|
932 | 76fda900 | Michael Hanselmann | |
933 | 76fda900 | Michael Hanselmann | |
934 | 76fda900 | Michael Hanselmann | def UseVirtualCluster(_cfg=None): |
935 | 76fda900 | Michael Hanselmann | """Returns whether a virtual cluster is used.
|
936 | 76fda900 | Michael Hanselmann |
|
937 | 76fda900 | Michael Hanselmann | @rtype: bool
|
938 | 76fda900 | Michael Hanselmann |
|
939 | 76fda900 | Michael Hanselmann | """
|
940 | 76fda900 | Michael Hanselmann | if _cfg is None: |
941 | 76fda900 | Michael Hanselmann | cfg = GetConfig() |
942 | 76fda900 | Michael Hanselmann | else:
|
943 | 76fda900 | Michael Hanselmann | cfg = _cfg |
944 | 76fda900 | Michael Hanselmann | |
945 | 76fda900 | Michael Hanselmann | (master, _) = cfg.GetVclusterSettings() |
946 | 76fda900 | Michael Hanselmann | |
947 | 76fda900 | Michael Hanselmann | return bool(master) |
948 | 76fda900 | Michael Hanselmann | |
949 | 76fda900 | Michael Hanselmann | |
950 | 76fda900 | Michael Hanselmann | @ht.WithDesc("No virtual cluster") |
951 | 76fda900 | Michael Hanselmann | def NoVirtualCluster(): |
952 | 76fda900 | Michael Hanselmann | """Used to disable tests for virtual clusters.
|
953 | 76fda900 | Michael Hanselmann |
|
954 | 76fda900 | Michael Hanselmann | """
|
955 | 76fda900 | Michael Hanselmann | return not UseVirtualCluster() |
956 | 090128b6 | Christos Stavrakakis | |
957 | 090128b6 | Christos Stavrakakis | |
958 | 090128b6 | Christos Stavrakakis | def GetDiskOptions(): |
959 | 090128b6 | Christos Stavrakakis | """Wrapper for L{_QaConfig.GetDiskOptions}.
|
960 | 090128b6 | Christos Stavrakakis |
|
961 | 090128b6 | Christos Stavrakakis | """
|
962 | 090128b6 | Christos Stavrakakis | return GetConfig().GetDiskOptions() |