root / test / ganeti.query_unittest.py @ 8235fe04
History | View | Annotate | Download (17.7 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2010 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 |
"""Script for testing ganeti.query"""
|
23 |
|
24 |
import re |
25 |
import unittest |
26 |
|
27 |
from ganeti import constants |
28 |
from ganeti import utils |
29 |
from ganeti import compat |
30 |
from ganeti import errors |
31 |
from ganeti import query |
32 |
from ganeti import objects |
33 |
|
34 |
import testutils |
35 |
|
36 |
|
37 |
class TestConstants(unittest.TestCase): |
38 |
def test(self): |
39 |
self.assertEqual(set(query._VERIFY_FN.keys()), |
40 |
constants.QFT_ALL) |
41 |
|
42 |
|
43 |
class _QueryData: |
44 |
def __init__(self, data, **kwargs): |
45 |
self.data = data
|
46 |
|
47 |
for name, value in kwargs.items(): |
48 |
setattr(self, name, value) |
49 |
|
50 |
def __iter__(self): |
51 |
return iter(self.data) |
52 |
|
53 |
|
54 |
def _GetDiskSize(nr, ctx, item): |
55 |
disks = item["disks"]
|
56 |
try:
|
57 |
return (constants.QRFS_NORMAL, disks[nr])
|
58 |
except IndexError: |
59 |
return (constants.QRFS_UNAVAIL, None) |
60 |
|
61 |
|
62 |
class TestQuery(unittest.TestCase): |
63 |
def test(self): |
64 |
(STATIC, DISK) = range(10, 12) |
65 |
|
66 |
fielddef = query._PrepareFieldList([ |
67 |
(query._MakeField("name", "Name", constants.QFT_TEXT), |
68 |
STATIC, lambda ctx, item: (constants.QRFS_NORMAL, item["name"])), |
69 |
(query._MakeField("master", "Master", constants.QFT_BOOL), |
70 |
STATIC, lambda ctx, item: (constants.QRFS_NORMAL,
|
71 |
ctx.mastername == item["name"])),
|
72 |
] + |
73 |
[(query._MakeField("disk%s.size" % i, "DiskSize%s" % i, |
74 |
constants.QFT_UNIT), |
75 |
DISK, compat.partial(_GetDiskSize, i)) |
76 |
for i in range(4)]) |
77 |
|
78 |
q = query.Query(fielddef, ["name"])
|
79 |
self.assertEqual(q.RequestedData(), set([STATIC])) |
80 |
self.assertEqual(len(q._fields), 1) |
81 |
self.assertEqual(len(q.GetFields()), 1) |
82 |
self.assertEqual(q.GetFields()[0].ToDict(), |
83 |
objects.QueryFieldDefinition(name="name",
|
84 |
title="Name",
|
85 |
kind=constants.QFT_TEXT).ToDict()) |
86 |
|
87 |
# Create data only once query has been prepared
|
88 |
data = [ |
89 |
{ "name": "node1", "disks": [0, 1, 2], }, |
90 |
{ "name": "node2", "disks": [3, 4], }, |
91 |
{ "name": "node3", "disks": [5, 6, 7], }, |
92 |
] |
93 |
|
94 |
self.assertEqual(q.Query(_QueryData(data, mastername="node3")), |
95 |
[[(constants.QRFS_NORMAL, "node1")],
|
96 |
[(constants.QRFS_NORMAL, "node2")],
|
97 |
[(constants.QRFS_NORMAL, "node3")]])
|
98 |
self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node3")), |
99 |
[["node1"], ["node2"], ["node3"]]) |
100 |
|
101 |
q = query.Query(fielddef, ["name", "master"]) |
102 |
self.assertEqual(q.RequestedData(), set([STATIC])) |
103 |
self.assertEqual(len(q._fields), 2) |
104 |
self.assertEqual(q.Query(_QueryData(data, mastername="node3")), |
105 |
[[(constants.QRFS_NORMAL, "node1"),
|
106 |
(constants.QRFS_NORMAL, False)],
|
107 |
[(constants.QRFS_NORMAL, "node2"),
|
108 |
(constants.QRFS_NORMAL, False)],
|
109 |
[(constants.QRFS_NORMAL, "node3"),
|
110 |
(constants.QRFS_NORMAL, True)],
|
111 |
]) |
112 |
|
113 |
q = query.Query(fielddef, ["name", "master", "disk0.size"]) |
114 |
self.assertEqual(q.RequestedData(), set([STATIC, DISK])) |
115 |
self.assertEqual(len(q._fields), 3) |
116 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
117 |
[[(constants.QRFS_NORMAL, "node1"),
|
118 |
(constants.QRFS_NORMAL, False),
|
119 |
(constants.QRFS_NORMAL, 0)],
|
120 |
[(constants.QRFS_NORMAL, "node2"),
|
121 |
(constants.QRFS_NORMAL, True),
|
122 |
(constants.QRFS_NORMAL, 3)],
|
123 |
[(constants.QRFS_NORMAL, "node3"),
|
124 |
(constants.QRFS_NORMAL, False),
|
125 |
(constants.QRFS_NORMAL, 5)],
|
126 |
]) |
127 |
|
128 |
# With unknown column
|
129 |
q = query.Query(fielddef, ["disk2.size", "disk1.size", "disk99.size", |
130 |
"disk0.size"])
|
131 |
self.assertEqual(q.RequestedData(), set([DISK])) |
132 |
self.assertEqual(len(q._fields), 4) |
133 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
134 |
[[(constants.QRFS_NORMAL, 2),
|
135 |
(constants.QRFS_NORMAL, 1),
|
136 |
(constants.QRFS_UNKNOWN, None),
|
137 |
(constants.QRFS_NORMAL, 0)],
|
138 |
[(constants.QRFS_UNAVAIL, None),
|
139 |
(constants.QRFS_NORMAL, 4),
|
140 |
(constants.QRFS_UNKNOWN, None),
|
141 |
(constants.QRFS_NORMAL, 3)],
|
142 |
[(constants.QRFS_NORMAL, 7),
|
143 |
(constants.QRFS_NORMAL, 6),
|
144 |
(constants.QRFS_UNKNOWN, None),
|
145 |
(constants.QRFS_NORMAL, 5)],
|
146 |
]) |
147 |
self.assertRaises(errors.OpPrereqError, q.OldStyleQuery,
|
148 |
_QueryData(data, mastername="node2"))
|
149 |
self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], [ |
150 |
{ "name": "disk2.size", "title": "DiskSize2", |
151 |
"kind": constants.QFT_UNIT, },
|
152 |
{ "name": "disk1.size", "title": "DiskSize1", |
153 |
"kind": constants.QFT_UNIT, },
|
154 |
{ "name": "disk99.size", "title": "disk99.size", |
155 |
"kind": constants.QFT_UNKNOWN, },
|
156 |
{ "name": "disk0.size", "title": "DiskSize0", |
157 |
"kind": constants.QFT_UNIT, },
|
158 |
]) |
159 |
|
160 |
# Empty query
|
161 |
q = query.Query(fielddef, []) |
162 |
self.assertEqual(q.RequestedData(), set([])) |
163 |
self.assertEqual(len(q._fields), 0) |
164 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
165 |
[[], [], []]) |
166 |
self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node2")), |
167 |
[[], [], []]) |
168 |
self.assertEqual(q.GetFields(), [])
|
169 |
|
170 |
def testPrepareFieldList(self): |
171 |
# Duplicate titles
|
172 |
for (a, b) in [("name", "name"), ("NAME", "name")]: |
173 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
174 |
(query._MakeField("name", b, constants.QFT_TEXT), None, |
175 |
lambda *args: None), |
176 |
(query._MakeField("other", a, constants.QFT_TEXT), None, |
177 |
lambda *args: None), |
178 |
]) |
179 |
|
180 |
# Non-lowercase names
|
181 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
182 |
(query._MakeField("NAME", "Name", constants.QFT_TEXT), None, |
183 |
lambda *args: None), |
184 |
]) |
185 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
186 |
(query._MakeField("Name", "Name", constants.QFT_TEXT), None, |
187 |
lambda *args: None), |
188 |
]) |
189 |
|
190 |
# Empty name
|
191 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
192 |
(query._MakeField("", "Name", constants.QFT_TEXT), None, |
193 |
lambda *args: None), |
194 |
]) |
195 |
|
196 |
# Empty title
|
197 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
198 |
(query._MakeField("name", "", constants.QFT_TEXT), None, |
199 |
lambda *args: None), |
200 |
]) |
201 |
|
202 |
# Whitespace in title
|
203 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
204 |
(query._MakeField("name", "Co lu mn", constants.QFT_TEXT), None, |
205 |
lambda *args: None), |
206 |
]) |
207 |
|
208 |
# No callable function
|
209 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
210 |
(query._MakeField("name", "Name", constants.QFT_TEXT), None, None), |
211 |
]) |
212 |
|
213 |
def testUnknown(self): |
214 |
fielddef = query._PrepareFieldList([ |
215 |
(query._MakeField("name", "Name", constants.QFT_TEXT), |
216 |
None, lambda _, item: (constants.QRFS_NORMAL, "name%s" % item)), |
217 |
(query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP), |
218 |
None, lambda *args: (constants.QRFS_NORMAL, 1234)), |
219 |
(query._MakeField("nodata", "NoData", constants.QFT_NUMBER), |
220 |
None, lambda *args: (constants.QRFS_NODATA, None)), |
221 |
(query._MakeField("unavail", "Unavail", constants.QFT_BOOL), |
222 |
None, lambda *args: (constants.QRFS_UNAVAIL, None)), |
223 |
]) |
224 |
|
225 |
for selected in [["foo"], ["Hello", "World"], |
226 |
["name1", "other", "foo"]]: |
227 |
q = query.Query(fielddef, selected) |
228 |
self.assertEqual(len(q._fields), len(selected)) |
229 |
self.assert_(compat.all(len(row) == len(selected) |
230 |
for row in q.Query(_QueryData(range(1, 10))))) |
231 |
self.assertEqual(q.Query(_QueryData(range(1, 10))), |
232 |
[[(constants.QRFS_UNKNOWN, None)] * len(selected) |
233 |
for i in range(1, 10)]) |
234 |
self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], |
235 |
[{ "name": name, "title": name, |
236 |
"kind": constants.QFT_UNKNOWN, }
|
237 |
for name in selected]) |
238 |
|
239 |
q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"]) |
240 |
self.assertEqual(len(q._fields), 4) |
241 |
self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [ |
242 |
["name%s" % i, 1234, None, None] |
243 |
for i in range(1, 10) |
244 |
]) |
245 |
|
246 |
q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"]) |
247 |
self.assertEqual(len(q._fields), 5) |
248 |
self.assertEqual(q.Query(_QueryData(range(1, 10))), |
249 |
[[(constants.QRFS_NORMAL, "name%s" % i),
|
250 |
(constants.QRFS_NORMAL, 1234),
|
251 |
(constants.QRFS_NODATA, None),
|
252 |
(constants.QRFS_UNAVAIL, None),
|
253 |
(constants.QRFS_UNKNOWN, None)]
|
254 |
for i in range(1, 10)]) |
255 |
|
256 |
|
257 |
class TestGetNodeRole(unittest.TestCase): |
258 |
def testMaster(self): |
259 |
node = objects.Node(name="node1")
|
260 |
self.assertEqual(query._GetNodeRole(node, "node1"), "M") |
261 |
|
262 |
def testMasterCandidate(self): |
263 |
node = objects.Node(name="node1", master_candidate=True) |
264 |
self.assertEqual(query._GetNodeRole(node, "master"), "C") |
265 |
|
266 |
def testRegular(self): |
267 |
node = objects.Node(name="node1")
|
268 |
self.assertEqual(query._GetNodeRole(node, "master"), "R") |
269 |
|
270 |
def testDrained(self): |
271 |
node = objects.Node(name="node1", drained=True) |
272 |
self.assertEqual(query._GetNodeRole(node, "master"), "D") |
273 |
|
274 |
def testOffline(self): |
275 |
node = objects.Node(name="node1", offline=True) |
276 |
self.assertEqual(query._GetNodeRole(node, "master"), "O") |
277 |
|
278 |
|
279 |
class TestNodeQuery(unittest.TestCase): |
280 |
def _Create(self, selected): |
281 |
return query.Query(query.NODE_FIELDS, selected)
|
282 |
|
283 |
def testSimple(self): |
284 |
nodes = [ |
285 |
objects.Node(name="node1", drained=False), |
286 |
objects.Node(name="node2", drained=True), |
287 |
objects.Node(name="node3", drained=False), |
288 |
] |
289 |
for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]: |
290 |
nqd = query.NodeQueryData(nodes, live_data, None, None, None, None) |
291 |
|
292 |
q = self._Create(["name", "drained"]) |
293 |
self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG])) |
294 |
self.assertEqual(q.Query(nqd),
|
295 |
[[(constants.QRFS_NORMAL, "node1"),
|
296 |
(constants.QRFS_NORMAL, False)],
|
297 |
[(constants.QRFS_NORMAL, "node2"),
|
298 |
(constants.QRFS_NORMAL, True)],
|
299 |
[(constants.QRFS_NORMAL, "node3"),
|
300 |
(constants.QRFS_NORMAL, False)],
|
301 |
]) |
302 |
self.assertEqual(q.OldStyleQuery(nqd),
|
303 |
[["node1", False], |
304 |
["node2", True], |
305 |
["node3", False]]) |
306 |
|
307 |
def test(self): |
308 |
selected = query.NODE_FIELDS.keys() |
309 |
field_index = dict((field, idx) for idx, field in enumerate(selected)) |
310 |
|
311 |
q = self._Create(selected)
|
312 |
self.assertEqual(q.RequestedData(),
|
313 |
set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
|
314 |
query.NQ_GROUP])) |
315 |
|
316 |
node_names = ["node%s" % i for i in range(20)] |
317 |
master_name = node_names[3]
|
318 |
nodes = [ |
319 |
objects.Node(name=name, |
320 |
primary_ip="192.0.2.%s" % idx,
|
321 |
secondary_ip="192.0.100.%s" % idx,
|
322 |
serial_no=7789 * idx,
|
323 |
master_candidate=(name != master_name and idx % 3 == 0), |
324 |
offline=False,
|
325 |
drained=False,
|
326 |
vm_capable=False,
|
327 |
master_capable=False,
|
328 |
group="default",
|
329 |
ctime=1290006900,
|
330 |
mtime=1290006913,
|
331 |
uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
|
332 |
for idx, name in enumerate(node_names) |
333 |
] |
334 |
|
335 |
master_node = nodes[3]
|
336 |
master_node.AddTag("masternode")
|
337 |
master_node.AddTag("another")
|
338 |
master_node.AddTag("tag")
|
339 |
assert master_node.name == master_name
|
340 |
|
341 |
live_data_name = node_names[4]
|
342 |
assert live_data_name != master_name
|
343 |
|
344 |
fake_live_data = { |
345 |
"bootid": "a2504766-498e-4b25-b21e-d23098dc3af4", |
346 |
"cnodes": 4, |
347 |
"csockets": 4, |
348 |
"ctotal": 8, |
349 |
"mnode": 128, |
350 |
"mfree": 100, |
351 |
"mtotal": 4096, |
352 |
"dfree": 5 * 1024 * 1024, |
353 |
"dtotal": 100 * 1024 * 1024, |
354 |
} |
355 |
|
356 |
assert (sorted(query._NODE_LIVE_FIELDS.keys()) == |
357 |
sorted(fake_live_data.keys()))
|
358 |
|
359 |
live_data = dict.fromkeys(node_names, {})
|
360 |
live_data[live_data_name] = \ |
361 |
dict((query._NODE_LIVE_FIELDS[name][2], value) |
362 |
for name, value in fake_live_data.items()) |
363 |
|
364 |
node_to_primary = dict((name, set()) for name in node_names) |
365 |
node_to_primary[master_name].update(["inst1", "inst2"]) |
366 |
|
367 |
node_to_secondary = dict((name, set()) for name in node_names) |
368 |
node_to_secondary[live_data_name].update(["instX", "instY", "instZ"]) |
369 |
|
370 |
ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
|
371 |
groups = { |
372 |
ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid),
|
373 |
} |
374 |
|
375 |
master_node.group = ng_uuid |
376 |
|
377 |
nqd = query.NodeQueryData(nodes, live_data, master_name, |
378 |
node_to_primary, node_to_secondary, groups) |
379 |
result = q.Query(nqd) |
380 |
self.assert_(compat.all(len(row) == len(selected) for row in result)) |
381 |
self.assertEqual([row[field_index["name"]] for row in result], |
382 |
[(constants.QRFS_NORMAL, name) for name in node_names]) |
383 |
|
384 |
node_to_row = dict((row[field_index["name"]][1], idx) |
385 |
for idx, row in enumerate(result)) |
386 |
|
387 |
master_row = result[node_to_row[master_name]] |
388 |
self.assert_(master_row[field_index["master"]]) |
389 |
self.assert_(master_row[field_index["role"]], "M") |
390 |
self.assertEqual(master_row[field_index["group"]], |
391 |
(constants.QRFS_NORMAL, "ng1"))
|
392 |
self.assertEqual(master_row[field_index["group.uuid"]], |
393 |
(constants.QRFS_NORMAL, ng_uuid)) |
394 |
|
395 |
self.assert_(row[field_index["pip"]] == node.primary_ip and |
396 |
row[field_index["sip"]] == node.secondary_ip and |
397 |
set(row[field_index["tags"]]) == node.GetTags() and |
398 |
row[field_index["serial_no"]] == node.serial_no and |
399 |
row[field_index["role"]] == query._GetNodeRole(node,
|
400 |
master_name) and
|
401 |
(node.name == master_name or
|
402 |
(row[field_index["group"]] == "<unknown>" and |
403 |
row[field_index["group.uuid"]] is None)) |
404 |
for row, node in zip(result, nodes)) |
405 |
|
406 |
live_data_row = result[node_to_row[live_data_name]] |
407 |
|
408 |
for (field, value) in fake_live_data.items(): |
409 |
self.assertEqual(live_data_row[field_index[field]],
|
410 |
(constants.QRFS_NORMAL, value)) |
411 |
|
412 |
self.assertEqual(master_row[field_index["pinst_cnt"]], |
413 |
(constants.QRFS_NORMAL, 2))
|
414 |
self.assertEqual(live_data_row[field_index["sinst_cnt"]], |
415 |
(constants.QRFS_NORMAL, 3))
|
416 |
self.assertEqual(master_row[field_index["pinst_list"]], |
417 |
(constants.QRFS_NORMAL, |
418 |
list(node_to_primary[master_name])))
|
419 |
self.assertEqual(live_data_row[field_index["sinst_list"]], |
420 |
(constants.QRFS_NORMAL, |
421 |
list(node_to_secondary[live_data_name])))
|
422 |
|
423 |
def testGetLiveNodeField(self): |
424 |
nodes = [ |
425 |
objects.Node(name="node1", drained=False), |
426 |
objects.Node(name="node2", drained=True), |
427 |
objects.Node(name="node3", drained=False), |
428 |
] |
429 |
live_data = dict.fromkeys([node.name for node in nodes], {}) |
430 |
|
431 |
# No data
|
432 |
nqd = query.NodeQueryData(None, None, None, None, None, None) |
433 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
434 |
nqd, None),
|
435 |
(constants.QRFS_NODATA, None))
|
436 |
|
437 |
# Missing field
|
438 |
ctx = _QueryData(None, curlive_data={
|
439 |
"some": 1, |
440 |
"other": 2, |
441 |
}) |
442 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
443 |
ctx, None),
|
444 |
(constants.QRFS_UNAVAIL, None))
|
445 |
|
446 |
# Wrong format/datatype
|
447 |
ctx = _QueryData(None, curlive_data={
|
448 |
"hello": ["Hello World"], |
449 |
"other": 2, |
450 |
}) |
451 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
452 |
ctx, None),
|
453 |
(constants.QRFS_UNAVAIL, None))
|
454 |
|
455 |
# Wrong field type
|
456 |
ctx = _QueryData(None, curlive_data={"hello": 123}) |
457 |
self.assertRaises(AssertionError, query._GetLiveNodeField, |
458 |
"hello", constants.QFT_BOOL, ctx, None) |
459 |
|
460 |
|
461 |
if __name__ == "__main__": |
462 |
testutils.GanetiTestProgram() |