root / test / ganeti.query_unittest.py @ 5d28cb6f
History | View | Annotate | Download (34.7 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2010, 2011 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 |
import random |
27 |
|
28 |
from ganeti import constants |
29 |
from ganeti import utils |
30 |
from ganeti import compat |
31 |
from ganeti import errors |
32 |
from ganeti import query |
33 |
from ganeti import objects |
34 |
from ganeti import cmdlib |
35 |
|
36 |
import testutils |
37 |
|
38 |
|
39 |
class TestConstants(unittest.TestCase): |
40 |
def test(self): |
41 |
self.assertEqual(set(query._VERIFY_FN.keys()), |
42 |
constants.QFT_ALL) |
43 |
|
44 |
|
45 |
class _QueryData: |
46 |
def __init__(self, data, **kwargs): |
47 |
self.data = data
|
48 |
|
49 |
for name, value in kwargs.items(): |
50 |
setattr(self, name, value) |
51 |
|
52 |
def __iter__(self): |
53 |
return iter(self.data) |
54 |
|
55 |
|
56 |
def _GetDiskSize(nr, ctx, item): |
57 |
disks = item["disks"]
|
58 |
try:
|
59 |
return disks[nr]
|
60 |
except IndexError: |
61 |
return query._FS_UNAVAIL
|
62 |
|
63 |
|
64 |
class TestQuery(unittest.TestCase): |
65 |
def test(self): |
66 |
(STATIC, DISK) = range(10, 12) |
67 |
|
68 |
fielddef = query._PrepareFieldList([ |
69 |
(query._MakeField("name", "Name", constants.QFT_TEXT), |
70 |
STATIC, lambda ctx, item: item["name"]), |
71 |
(query._MakeField("master", "Master", constants.QFT_BOOL), |
72 |
STATIC, lambda ctx, item: ctx.mastername == item["name"]), |
73 |
] + |
74 |
[(query._MakeField("disk%s.size" % i, "DiskSize%s" % i, |
75 |
constants.QFT_UNIT), |
76 |
DISK, compat.partial(_GetDiskSize, i)) |
77 |
for i in range(4)], []) |
78 |
|
79 |
q = query.Query(fielddef, ["name"])
|
80 |
self.assertEqual(q.RequestedData(), set([STATIC])) |
81 |
self.assertEqual(len(q._fields), 1) |
82 |
self.assertEqual(len(q.GetFields()), 1) |
83 |
self.assertEqual(q.GetFields()[0].ToDict(), |
84 |
objects.QueryFieldDefinition(name="name",
|
85 |
title="Name",
|
86 |
kind=constants.QFT_TEXT).ToDict()) |
87 |
|
88 |
# Create data only once query has been prepared
|
89 |
data = [ |
90 |
{ "name": "node1", "disks": [0, 1, 2], }, |
91 |
{ "name": "node2", "disks": [3, 4], }, |
92 |
{ "name": "node3", "disks": [5, 6, 7], }, |
93 |
] |
94 |
|
95 |
self.assertEqual(q.Query(_QueryData(data, mastername="node3")), |
96 |
[[(constants.RS_NORMAL, "node1")],
|
97 |
[(constants.RS_NORMAL, "node2")],
|
98 |
[(constants.RS_NORMAL, "node3")]])
|
99 |
self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node3")), |
100 |
[["node1"], ["node2"], ["node3"]]) |
101 |
|
102 |
q = query.Query(fielddef, ["name", "master"]) |
103 |
self.assertEqual(q.RequestedData(), set([STATIC])) |
104 |
self.assertEqual(len(q._fields), 2) |
105 |
self.assertEqual(q.Query(_QueryData(data, mastername="node3")), |
106 |
[[(constants.RS_NORMAL, "node1"),
|
107 |
(constants.RS_NORMAL, False)],
|
108 |
[(constants.RS_NORMAL, "node2"),
|
109 |
(constants.RS_NORMAL, False)],
|
110 |
[(constants.RS_NORMAL, "node3"),
|
111 |
(constants.RS_NORMAL, True)],
|
112 |
]) |
113 |
|
114 |
q = query.Query(fielddef, ["name", "master", "disk0.size"]) |
115 |
self.assertEqual(q.RequestedData(), set([STATIC, DISK])) |
116 |
self.assertEqual(len(q._fields), 3) |
117 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
118 |
[[(constants.RS_NORMAL, "node1"),
|
119 |
(constants.RS_NORMAL, False),
|
120 |
(constants.RS_NORMAL, 0)],
|
121 |
[(constants.RS_NORMAL, "node2"),
|
122 |
(constants.RS_NORMAL, True),
|
123 |
(constants.RS_NORMAL, 3)],
|
124 |
[(constants.RS_NORMAL, "node3"),
|
125 |
(constants.RS_NORMAL, False),
|
126 |
(constants.RS_NORMAL, 5)],
|
127 |
]) |
128 |
|
129 |
# With unknown column
|
130 |
q = query.Query(fielddef, ["disk2.size", "disk1.size", "disk99.size", |
131 |
"disk0.size"])
|
132 |
self.assertEqual(q.RequestedData(), set([DISK])) |
133 |
self.assertEqual(len(q._fields), 4) |
134 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
135 |
[[(constants.RS_NORMAL, 2),
|
136 |
(constants.RS_NORMAL, 1),
|
137 |
(constants.RS_UNKNOWN, None),
|
138 |
(constants.RS_NORMAL, 0)],
|
139 |
[(constants.RS_UNAVAIL, None),
|
140 |
(constants.RS_NORMAL, 4),
|
141 |
(constants.RS_UNKNOWN, None),
|
142 |
(constants.RS_NORMAL, 3)],
|
143 |
[(constants.RS_NORMAL, 7),
|
144 |
(constants.RS_NORMAL, 6),
|
145 |
(constants.RS_UNKNOWN, None),
|
146 |
(constants.RS_NORMAL, 5)],
|
147 |
]) |
148 |
self.assertRaises(errors.OpPrereqError, q.OldStyleQuery,
|
149 |
_QueryData(data, mastername="node2"))
|
150 |
self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], [ |
151 |
{ "name": "disk2.size", "title": "DiskSize2", |
152 |
"kind": constants.QFT_UNIT, },
|
153 |
{ "name": "disk1.size", "title": "DiskSize1", |
154 |
"kind": constants.QFT_UNIT, },
|
155 |
{ "name": "disk99.size", "title": "disk99.size", |
156 |
"kind": constants.QFT_UNKNOWN, },
|
157 |
{ "name": "disk0.size", "title": "DiskSize0", |
158 |
"kind": constants.QFT_UNIT, },
|
159 |
]) |
160 |
|
161 |
# Empty query
|
162 |
q = query.Query(fielddef, []) |
163 |
self.assertEqual(q.RequestedData(), set([])) |
164 |
self.assertEqual(len(q._fields), 0) |
165 |
self.assertEqual(q.Query(_QueryData(data, mastername="node2")), |
166 |
[[], [], []]) |
167 |
self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node2")), |
168 |
[[], [], []]) |
169 |
self.assertEqual(q.GetFields(), [])
|
170 |
|
171 |
def testPrepareFieldList(self): |
172 |
# Duplicate titles
|
173 |
for (a, b) in [("name", "name"), ("NAME", "name")]: |
174 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
175 |
(query._MakeField("name", b, constants.QFT_TEXT), None, |
176 |
lambda *args: None), |
177 |
(query._MakeField("other", a, constants.QFT_TEXT), None, |
178 |
lambda *args: None), |
179 |
], []) |
180 |
|
181 |
# Non-lowercase names
|
182 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
183 |
(query._MakeField("NAME", "Name", constants.QFT_TEXT), None, |
184 |
lambda *args: None), |
185 |
], []) |
186 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
187 |
(query._MakeField("Name", "Name", constants.QFT_TEXT), None, |
188 |
lambda *args: None), |
189 |
], []) |
190 |
|
191 |
# Empty name
|
192 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
193 |
(query._MakeField("", "Name", constants.QFT_TEXT), None, |
194 |
lambda *args: None), |
195 |
], []) |
196 |
|
197 |
# Empty title
|
198 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
199 |
(query._MakeField("name", "", constants.QFT_TEXT), None, |
200 |
lambda *args: None), |
201 |
], []) |
202 |
|
203 |
# Whitespace in title
|
204 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
205 |
(query._MakeField("name", "Co lu mn", constants.QFT_TEXT), None, |
206 |
lambda *args: None), |
207 |
], []) |
208 |
|
209 |
# No callable function
|
210 |
self.assertRaises(AssertionError, query._PrepareFieldList, [ |
211 |
(query._MakeField("name", "Name", constants.QFT_TEXT), None, None), |
212 |
], []) |
213 |
|
214 |
def testUnknown(self): |
215 |
fielddef = query._PrepareFieldList([ |
216 |
(query._MakeField("name", "Name", constants.QFT_TEXT), |
217 |
None, lambda _, item: "name%s" % item), |
218 |
(query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP), |
219 |
None, lambda *args: 1234), |
220 |
(query._MakeField("nodata", "NoData", constants.QFT_NUMBER), |
221 |
None, lambda *args: query._FS_NODATA ), |
222 |
(query._MakeField("unavail", "Unavail", constants.QFT_BOOL), |
223 |
None, lambda *args: query._FS_UNAVAIL), |
224 |
], []) |
225 |
|
226 |
for selected in [["foo"], ["Hello", "World"], |
227 |
["name1", "other", "foo"]]: |
228 |
q = query.Query(fielddef, selected) |
229 |
self.assertEqual(len(q._fields), len(selected)) |
230 |
self.assert_(compat.all(len(row) == len(selected) |
231 |
for row in q.Query(_QueryData(range(1, 10))))) |
232 |
self.assertEqual(q.Query(_QueryData(range(1, 10))), |
233 |
[[(constants.RS_UNKNOWN, None)] * len(selected) |
234 |
for i in range(1, 10)]) |
235 |
self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], |
236 |
[{ "name": name, "title": name, |
237 |
"kind": constants.QFT_UNKNOWN, }
|
238 |
for name in selected]) |
239 |
|
240 |
q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"]) |
241 |
self.assertEqual(len(q._fields), 4) |
242 |
self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [ |
243 |
["name%s" % i, 1234, None, None] |
244 |
for i in range(1, 10) |
245 |
]) |
246 |
|
247 |
q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"]) |
248 |
self.assertEqual(len(q._fields), 5) |
249 |
self.assertEqual(q.Query(_QueryData(range(1, 10))), |
250 |
[[(constants.RS_NORMAL, "name%s" % i),
|
251 |
(constants.RS_NORMAL, 1234),
|
252 |
(constants.RS_NODATA, None),
|
253 |
(constants.RS_UNAVAIL, None),
|
254 |
(constants.RS_UNKNOWN, None)]
|
255 |
for i in range(1, 10)]) |
256 |
|
257 |
def testAliases(self): |
258 |
fields = [ |
259 |
(query._MakeField("a", "a-title", constants.QFT_TEXT), None, |
260 |
lambda *args: None), |
261 |
(query._MakeField("b", "b-title", constants.QFT_TEXT), None, |
262 |
lambda *args: None), |
263 |
] |
264 |
# duplicate field
|
265 |
self.assertRaises(AssertionError, query._PrepareFieldList, fields, |
266 |
[("b", "a")]) |
267 |
self.assertRaises(AssertionError, query._PrepareFieldList, fields, |
268 |
[("c", "b"), ("c", "a")]) |
269 |
# missing target
|
270 |
self.assertRaises(AssertionError, query._PrepareFieldList, fields, |
271 |
[("c", "d")]) |
272 |
fdefs = query._PrepareFieldList(fields, [("c", "b")]) |
273 |
self.assertEqual(len(fdefs), 3) |
274 |
self.assertEqual(fdefs["b"][1:], fdefs["c"][1:]) |
275 |
|
276 |
|
277 |
class TestGetNodeRole(unittest.TestCase): |
278 |
def testMaster(self): |
279 |
node = objects.Node(name="node1")
|
280 |
self.assertEqual(query._GetNodeRole(node, "node1"), "M") |
281 |
|
282 |
def testMasterCandidate(self): |
283 |
node = objects.Node(name="node1", master_candidate=True) |
284 |
self.assertEqual(query._GetNodeRole(node, "master"), "C") |
285 |
|
286 |
def testRegular(self): |
287 |
node = objects.Node(name="node1")
|
288 |
self.assertEqual(query._GetNodeRole(node, "master"), "R") |
289 |
|
290 |
def testDrained(self): |
291 |
node = objects.Node(name="node1", drained=True) |
292 |
self.assertEqual(query._GetNodeRole(node, "master"), "D") |
293 |
|
294 |
def testOffline(self): |
295 |
node = objects.Node(name="node1", offline=True) |
296 |
self.assertEqual(query._GetNodeRole(node, "master"), "O") |
297 |
|
298 |
|
299 |
class TestNodeQuery(unittest.TestCase): |
300 |
def _Create(self, selected): |
301 |
return query.Query(query.NODE_FIELDS, selected)
|
302 |
|
303 |
def testSimple(self): |
304 |
nodes = [ |
305 |
objects.Node(name="node1", drained=False), |
306 |
objects.Node(name="node2", drained=True), |
307 |
objects.Node(name="node3", drained=False), |
308 |
] |
309 |
for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]: |
310 |
nqd = query.NodeQueryData(nodes, live_data, None, None, None, None, None, |
311 |
None)
|
312 |
|
313 |
q = self._Create(["name", "drained"]) |
314 |
self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG])) |
315 |
self.assertEqual(q.Query(nqd),
|
316 |
[[(constants.RS_NORMAL, "node1"),
|
317 |
(constants.RS_NORMAL, False)],
|
318 |
[(constants.RS_NORMAL, "node2"),
|
319 |
(constants.RS_NORMAL, True)],
|
320 |
[(constants.RS_NORMAL, "node3"),
|
321 |
(constants.RS_NORMAL, False)],
|
322 |
]) |
323 |
self.assertEqual(q.OldStyleQuery(nqd),
|
324 |
[["node1", False], |
325 |
["node2", True], |
326 |
["node3", False]]) |
327 |
|
328 |
def test(self): |
329 |
selected = query.NODE_FIELDS.keys() |
330 |
field_index = dict((field, idx) for idx, field in enumerate(selected)) |
331 |
|
332 |
q = self._Create(selected)
|
333 |
self.assertEqual(q.RequestedData(),
|
334 |
set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
|
335 |
query.NQ_GROUP, query.NQ_OOB])) |
336 |
|
337 |
cluster = objects.Cluster(cluster_name="testcluster",
|
338 |
hvparams=constants.HVC_DEFAULTS, |
339 |
beparams={ |
340 |
constants.PP_DEFAULT: constants.BEC_DEFAULTS, |
341 |
}, |
342 |
nicparams={ |
343 |
constants.PP_DEFAULT: constants.NICC_DEFAULTS, |
344 |
}, |
345 |
ndparams=constants.NDC_DEFAULTS, |
346 |
) |
347 |
|
348 |
node_names = ["node%s" % i for i in range(20)] |
349 |
master_name = node_names[3]
|
350 |
nodes = [ |
351 |
objects.Node(name=name, |
352 |
primary_ip="192.0.2.%s" % idx,
|
353 |
secondary_ip="192.0.100.%s" % idx,
|
354 |
serial_no=7789 * idx,
|
355 |
master_candidate=(name != master_name and idx % 3 == 0), |
356 |
offline=False,
|
357 |
drained=False,
|
358 |
vm_capable=False,
|
359 |
master_capable=False,
|
360 |
ndparams={}, |
361 |
group="default",
|
362 |
ctime=1290006900,
|
363 |
mtime=1290006913,
|
364 |
uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
|
365 |
for idx, name in enumerate(node_names) |
366 |
] |
367 |
|
368 |
master_node = nodes[3]
|
369 |
master_node.AddTag("masternode")
|
370 |
master_node.AddTag("another")
|
371 |
master_node.AddTag("tag")
|
372 |
master_node.ctime = None
|
373 |
master_node.mtime = None
|
374 |
assert master_node.name == master_name
|
375 |
|
376 |
live_data_name = node_names[4]
|
377 |
assert live_data_name != master_name
|
378 |
|
379 |
fake_live_data = { |
380 |
"bootid": "a2504766-498e-4b25-b21e-d23098dc3af4", |
381 |
"cnodes": 4, |
382 |
"csockets": 4, |
383 |
"ctotal": 8, |
384 |
"mnode": 128, |
385 |
"mfree": 100, |
386 |
"mtotal": 4096, |
387 |
"dfree": 5 * 1024 * 1024, |
388 |
"dtotal": 100 * 1024 * 1024, |
389 |
} |
390 |
|
391 |
assert (sorted(query._NODE_LIVE_FIELDS.keys()) == |
392 |
sorted(fake_live_data.keys()))
|
393 |
|
394 |
live_data = dict.fromkeys(node_names, {})
|
395 |
live_data[live_data_name] = \ |
396 |
dict((query._NODE_LIVE_FIELDS[name][2], value) |
397 |
for name, value in fake_live_data.items()) |
398 |
|
399 |
node_to_primary = dict((name, set()) for name in node_names) |
400 |
node_to_primary[master_name].update(["inst1", "inst2"]) |
401 |
|
402 |
node_to_secondary = dict((name, set()) for name in node_names) |
403 |
node_to_secondary[live_data_name].update(["instX", "instY", "instZ"]) |
404 |
|
405 |
ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
|
406 |
groups = { |
407 |
ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
|
408 |
} |
409 |
|
410 |
oob_support = dict((name, False) for name in node_names) |
411 |
|
412 |
master_node.group = ng_uuid |
413 |
|
414 |
nqd = query.NodeQueryData(nodes, live_data, master_name, |
415 |
node_to_primary, node_to_secondary, groups, |
416 |
oob_support, cluster) |
417 |
result = q.Query(nqd) |
418 |
self.assert_(compat.all(len(row) == len(selected) for row in result)) |
419 |
self.assertEqual([row[field_index["name"]] for row in result], |
420 |
[(constants.RS_NORMAL, name) for name in node_names]) |
421 |
|
422 |
node_to_row = dict((row[field_index["name"]][1], idx) |
423 |
for idx, row in enumerate(result)) |
424 |
|
425 |
master_row = result[node_to_row[master_name]] |
426 |
self.assert_(master_row[field_index["master"]]) |
427 |
self.assert_(master_row[field_index["role"]], "M") |
428 |
self.assertEqual(master_row[field_index["group"]], |
429 |
(constants.RS_NORMAL, "ng1"))
|
430 |
self.assertEqual(master_row[field_index["group.uuid"]], |
431 |
(constants.RS_NORMAL, ng_uuid)) |
432 |
self.assertEqual(master_row[field_index["ctime"]], |
433 |
(constants.RS_UNAVAIL, None))
|
434 |
self.assertEqual(master_row[field_index["mtime"]], |
435 |
(constants.RS_UNAVAIL, None))
|
436 |
|
437 |
self.assert_(row[field_index["pip"]] == node.primary_ip and |
438 |
row[field_index["sip"]] == node.secondary_ip and |
439 |
set(row[field_index["tags"]]) == node.GetTags() and |
440 |
row[field_index["serial_no"]] == node.serial_no and |
441 |
row[field_index["role"]] == query._GetNodeRole(node,
|
442 |
master_name) and
|
443 |
(node.name == master_name or
|
444 |
(row[field_index["group"]] == "<unknown>" and |
445 |
row[field_index["group.uuid"]] is None and |
446 |
row[field_index["ctime"]] == (constants.RS_NORMAL,
|
447 |
node.ctime) and
|
448 |
row[field_index["mtime"]] == (constants.RS_NORMAL,
|
449 |
node.mtime))) |
450 |
for row, node in zip(result, nodes)) |
451 |
|
452 |
live_data_row = result[node_to_row[live_data_name]] |
453 |
|
454 |
for (field, value) in fake_live_data.items(): |
455 |
self.assertEqual(live_data_row[field_index[field]],
|
456 |
(constants.RS_NORMAL, value)) |
457 |
|
458 |
self.assertEqual(master_row[field_index["pinst_cnt"]], |
459 |
(constants.RS_NORMAL, 2))
|
460 |
self.assertEqual(live_data_row[field_index["sinst_cnt"]], |
461 |
(constants.RS_NORMAL, 3))
|
462 |
self.assertEqual(master_row[field_index["pinst_list"]], |
463 |
(constants.RS_NORMAL, |
464 |
list(node_to_primary[master_name])))
|
465 |
self.assertEqual(live_data_row[field_index["sinst_list"]], |
466 |
(constants.RS_NORMAL, |
467 |
list(node_to_secondary[live_data_name])))
|
468 |
|
469 |
def testGetLiveNodeField(self): |
470 |
nodes = [ |
471 |
objects.Node(name="node1", drained=False, offline=False), |
472 |
objects.Node(name="node2", drained=True, offline=False), |
473 |
objects.Node(name="node3", drained=False, offline=False), |
474 |
objects.Node(name="node4", drained=False, offline=True), |
475 |
] |
476 |
live_data = dict.fromkeys([node.name for node in nodes], {}) |
477 |
|
478 |
# No data
|
479 |
nqd = query.NodeQueryData(None, None, None, None, None, None, None, None) |
480 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
481 |
nqd, nodes[0]),
|
482 |
query._FS_NODATA) |
483 |
|
484 |
# Missing field
|
485 |
ctx = _QueryData(None, curlive_data={
|
486 |
"some": 1, |
487 |
"other": 2, |
488 |
}) |
489 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
490 |
ctx, nodes[0]),
|
491 |
query._FS_UNAVAIL) |
492 |
|
493 |
# Wrong format/datatype
|
494 |
ctx = _QueryData(None, curlive_data={
|
495 |
"hello": ["Hello World"], |
496 |
"other": 2, |
497 |
}) |
498 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
499 |
ctx, nodes[0]),
|
500 |
query._FS_UNAVAIL) |
501 |
|
502 |
# Offline node
|
503 |
assert nodes[3].offline |
504 |
ctx = _QueryData(None, curlive_data={})
|
505 |
self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER, |
506 |
ctx, nodes[3]),
|
507 |
query._FS_OFFLINE, None)
|
508 |
|
509 |
# Wrong field type
|
510 |
ctx = _QueryData(None, curlive_data={"hello": 123}) |
511 |
self.assertRaises(AssertionError, query._GetLiveNodeField, |
512 |
"hello", constants.QFT_BOOL, ctx, nodes[0]) |
513 |
|
514 |
|
515 |
class TestInstanceQuery(unittest.TestCase): |
516 |
def _Create(self, selected): |
517 |
return query.Query(query.INSTANCE_FIELDS, selected)
|
518 |
|
519 |
def testSimple(self): |
520 |
q = self._Create(["name", "be/memory", "ip"]) |
521 |
self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG])) |
522 |
|
523 |
cluster = objects.Cluster(cluster_name="testcluster",
|
524 |
hvparams=constants.HVC_DEFAULTS, |
525 |
beparams={ |
526 |
constants.PP_DEFAULT: constants.BEC_DEFAULTS, |
527 |
}, |
528 |
nicparams={ |
529 |
constants.PP_DEFAULT: constants.NICC_DEFAULTS, |
530 |
}) |
531 |
|
532 |
instances = [ |
533 |
objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[]),
|
534 |
objects.Instance(name="inst2", hvparams={}, nics=[],
|
535 |
beparams={ |
536 |
constants.BE_MEMORY: 512,
|
537 |
}), |
538 |
objects.Instance(name="inst3", hvparams={}, beparams={},
|
539 |
nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
|
540 |
] |
541 |
|
542 |
iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
|
543 |
set(), {})
|
544 |
self.assertEqual(q.Query(iqd),
|
545 |
[[(constants.RS_NORMAL, "inst1"),
|
546 |
(constants.RS_NORMAL, 128),
|
547 |
(constants.RS_UNAVAIL, None),
|
548 |
], |
549 |
[(constants.RS_NORMAL, "inst2"),
|
550 |
(constants.RS_NORMAL, 512),
|
551 |
(constants.RS_UNAVAIL, None),
|
552 |
], |
553 |
[(constants.RS_NORMAL, "inst3"),
|
554 |
(constants.RS_NORMAL, 128),
|
555 |
(constants.RS_NORMAL, "192.0.2.99"),
|
556 |
]]) |
557 |
self.assertEqual(q.OldStyleQuery(iqd),
|
558 |
[["inst1", 128, None], |
559 |
["inst2", 512, None], |
560 |
["inst3", 128, "192.0.2.99"]]) |
561 |
|
562 |
def test(self): |
563 |
selected = query.INSTANCE_FIELDS.keys() |
564 |
fieldidx = dict((field, idx) for idx, field in enumerate(selected)) |
565 |
|
566 |
macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255) |
567 |
for i in range(20)] |
568 |
|
569 |
q = self._Create(selected)
|
570 |
self.assertEqual(q.RequestedData(),
|
571 |
set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
|
572 |
query.IQ_CONSOLE])) |
573 |
|
574 |
cluster = objects.Cluster(cluster_name="testcluster",
|
575 |
hvparams=constants.HVC_DEFAULTS, |
576 |
beparams={ |
577 |
constants.PP_DEFAULT: constants.BEC_DEFAULTS, |
578 |
}, |
579 |
nicparams={ |
580 |
constants.PP_DEFAULT: constants.NICC_DEFAULTS, |
581 |
}, |
582 |
os_hvp={}, |
583 |
tcpudp_port_pool=set())
|
584 |
|
585 |
offline_nodes = ["nodeoff1", "nodeoff2"] |
586 |
bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes |
587 |
nodes = ["node%s" % i for i in range(10)] + bad_nodes |
588 |
|
589 |
instances = [ |
590 |
objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
|
591 |
uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
|
592 |
ctime=1291244000, mtime=1291244400, serial_no=30, |
593 |
admin_up=True, hypervisor=constants.HT_XEN_PVM, os="linux1", |
594 |
primary_node="node1",
|
595 |
disk_template=constants.DT_PLAIN, |
596 |
disks=[]), |
597 |
objects.Instance(name="inst2", hvparams={}, nics=[],
|
598 |
uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
|
599 |
ctime=1291211000, mtime=1291211077, serial_no=1, |
600 |
admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99", |
601 |
primary_node="node5",
|
602 |
disk_template=constants.DT_DISKLESS, |
603 |
disks=[], |
604 |
beparams={ |
605 |
constants.BE_MEMORY: 512,
|
606 |
}), |
607 |
objects.Instance(name="inst3", hvparams={}, beparams={},
|
608 |
uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
|
609 |
ctime=1291011000, mtime=1291013000, serial_no=1923, |
610 |
admin_up=False, hypervisor=constants.HT_KVM, os="busybox", |
611 |
primary_node="node6",
|
612 |
disk_template=constants.DT_DRBD8, |
613 |
disks=[], |
614 |
nics=[ |
615 |
objects.NIC(ip="192.0.2.99", mac=macs.pop(),
|
616 |
nicparams={ |
617 |
constants.NIC_LINK: constants.DEFAULT_BRIDGE, |
618 |
}), |
619 |
objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
|
620 |
]), |
621 |
objects.Instance(name="inst4", hvparams={}, beparams={},
|
622 |
uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
|
623 |
ctime=1291244390, mtime=1291244395, serial_no=25, |
624 |
admin_up=False, hypervisor=constants.HT_XEN_PVM, os="linux1", |
625 |
primary_node="nodeoff2",
|
626 |
disk_template=constants.DT_DRBD8, |
627 |
disks=[], |
628 |
nics=[ |
629 |
objects.NIC(ip="192.0.2.1", mac=macs.pop(),
|
630 |
nicparams={ |
631 |
constants.NIC_LINK: constants.DEFAULT_BRIDGE, |
632 |
}), |
633 |
objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
|
634 |
objects.NIC(ip="192.0.2.3", mac=macs.pop(),
|
635 |
nicparams={ |
636 |
constants.NIC_MODE: constants.NIC_MODE_ROUTED, |
637 |
}), |
638 |
objects.NIC(ip="192.0.2.4", mac=macs.pop(),
|
639 |
nicparams={ |
640 |
constants.NIC_MODE: constants.NIC_MODE_BRIDGED, |
641 |
constants.NIC_LINK: "eth123",
|
642 |
}), |
643 |
]), |
644 |
objects.Instance(name="inst5", hvparams={}, nics=[],
|
645 |
uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
|
646 |
ctime=1231211000, mtime=1261200000, serial_no=3, |
647 |
admin_up=True, hypervisor=constants.HT_XEN_HVM, os="deb99", |
648 |
primary_node="nodebad2",
|
649 |
disk_template=constants.DT_DISKLESS, |
650 |
disks=[], |
651 |
beparams={ |
652 |
constants.BE_MEMORY: 512,
|
653 |
}), |
654 |
objects.Instance(name="inst6", hvparams={}, nics=[],
|
655 |
uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
|
656 |
ctime=7513, mtime=11501, serial_no=13390, |
657 |
admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99", |
658 |
primary_node="node7",
|
659 |
disk_template=constants.DT_DISKLESS, |
660 |
disks=[], |
661 |
beparams={ |
662 |
constants.BE_MEMORY: 768,
|
663 |
}), |
664 |
objects.Instance(name="inst7", hvparams={}, nics=[],
|
665 |
uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
|
666 |
ctime=None, mtime=None, serial_no=1947, |
667 |
admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99", |
668 |
primary_node="node6",
|
669 |
disk_template=constants.DT_DISKLESS, |
670 |
disks=[], |
671 |
beparams={}), |
672 |
] |
673 |
|
674 |
assert not utils.FindDuplicates(inst.name for inst in instances) |
675 |
|
676 |
instbyname = dict((inst.name, inst) for inst in instances) |
677 |
|
678 |
disk_usage = dict((inst.name,
|
679 |
cmdlib._ComputeDiskSize(inst.disk_template, |
680 |
[{"size": disk.size}
|
681 |
for disk in inst.disks])) |
682 |
for inst in instances) |
683 |
|
684 |
inst_bridges = { |
685 |
"inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
|
686 |
"inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
|
687 |
None, "eth123"], |
688 |
} |
689 |
|
690 |
live_data = { |
691 |
"inst2": {
|
692 |
"vcpus": 3, |
693 |
}, |
694 |
"inst4": {
|
695 |
"memory": 123, |
696 |
}, |
697 |
"inst6": {
|
698 |
"memory": 768, |
699 |
}, |
700 |
} |
701 |
wrongnode_inst = set("inst2") |
702 |
|
703 |
consinfo = dict((inst.name, None) for inst in instances) |
704 |
consinfo["inst7"] = \
|
705 |
objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
|
706 |
host=instbyname["inst7"].primary_node,
|
707 |
user=constants.GANETI_RUNAS, |
708 |
command=["hostname"]).ToDict()
|
709 |
|
710 |
iqd = query.InstanceQueryData(instances, cluster, disk_usage, |
711 |
offline_nodes, bad_nodes, live_data, |
712 |
wrongnode_inst, consinfo) |
713 |
result = q.Query(iqd) |
714 |
self.assertEqual(len(result), len(instances)) |
715 |
self.assert_(compat.all(len(row) == len(selected) |
716 |
for row in result)) |
717 |
|
718 |
assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \ |
719 |
"Offline nodes not included in bad nodes"
|
720 |
|
721 |
tested_status = set()
|
722 |
|
723 |
for (inst, row) in zip(instances, result): |
724 |
assert inst.primary_node in nodes |
725 |
|
726 |
self.assertEqual(row[fieldidx["name"]], |
727 |
(constants.RS_NORMAL, inst.name)) |
728 |
|
729 |
if inst.primary_node in offline_nodes: |
730 |
exp_status = "ERROR_nodeoffline"
|
731 |
elif inst.primary_node in bad_nodes: |
732 |
exp_status = "ERROR_nodedown"
|
733 |
elif inst.name in live_data: |
734 |
if inst.name in wrongnode_inst: |
735 |
exp_status = "ERROR_wrongnode"
|
736 |
elif inst.admin_up:
|
737 |
exp_status = "running"
|
738 |
else:
|
739 |
exp_status = "ERROR_up"
|
740 |
elif inst.admin_up:
|
741 |
exp_status = "ERROR_down"
|
742 |
else:
|
743 |
exp_status = "ADMIN_down"
|
744 |
|
745 |
self.assertEqual(row[fieldidx["status"]], |
746 |
(constants.RS_NORMAL, exp_status)) |
747 |
|
748 |
(_, status) = row[fieldidx["status"]]
|
749 |
tested_status.add(status) |
750 |
|
751 |
for (field, livefield) in [("oper_ram", "memory"), |
752 |
("oper_vcpus", "vcpus")]: |
753 |
if inst.primary_node in bad_nodes: |
754 |
exp = (constants.RS_NODATA, None)
|
755 |
elif inst.name in live_data: |
756 |
value = live_data[inst.name].get(livefield, None)
|
757 |
if value is None: |
758 |
exp = (constants.RS_UNAVAIL, None)
|
759 |
else:
|
760 |
exp = (constants.RS_NORMAL, value) |
761 |
else:
|
762 |
exp = (constants.RS_UNAVAIL, None)
|
763 |
|
764 |
self.assertEqual(row[fieldidx[field]], exp)
|
765 |
|
766 |
bridges = inst_bridges.get(inst.name, []) |
767 |
self.assertEqual(row[fieldidx["nic.bridges"]], |
768 |
(constants.RS_NORMAL, bridges)) |
769 |
if bridges:
|
770 |
self.assertEqual(row[fieldidx["bridge"]], |
771 |
(constants.RS_NORMAL, bridges[0]))
|
772 |
else:
|
773 |
self.assertEqual(row[fieldidx["bridge"]], |
774 |
(constants.RS_UNAVAIL, None))
|
775 |
|
776 |
for i in range(constants.MAX_NICS): |
777 |
if i < len(bridges) and bridges[i] is not None: |
778 |
exp = (constants.RS_NORMAL, bridges[i]) |
779 |
else:
|
780 |
exp = (constants.RS_UNAVAIL, None)
|
781 |
self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp) |
782 |
|
783 |
if inst.primary_node in bad_nodes: |
784 |
exp = (constants.RS_NODATA, None)
|
785 |
else:
|
786 |
exp = (constants.RS_NORMAL, inst.name in live_data)
|
787 |
self.assertEqual(row[fieldidx["oper_state"]], exp) |
788 |
|
789 |
usage = disk_usage[inst.name] |
790 |
if usage is None: |
791 |
usage = 0
|
792 |
self.assertEqual(row[fieldidx["disk_usage"]], |
793 |
(constants.RS_NORMAL, usage)) |
794 |
|
795 |
self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]]) |
796 |
self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]]) |
797 |
|
798 |
for field in ["ctime", "mtime"]: |
799 |
if getattr(inst, field) is None: |
800 |
# No ctime/mtime
|
801 |
exp = (constants.RS_UNAVAIL, None)
|
802 |
else:
|
803 |
exp = (constants.RS_NORMAL, getattr(inst, field))
|
804 |
self.assertEqual(row[fieldidx[field]], exp)
|
805 |
|
806 |
self._CheckInstanceConsole(inst, row[fieldidx["console"]]) |
807 |
|
808 |
# Ensure all possible status' have been tested
|
809 |
self.assertEqual(tested_status,
|
810 |
set(["ERROR_nodeoffline", "ERROR_nodedown", |
811 |
"running", "ERROR_up", "ERROR_down", |
812 |
"ADMIN_down"]))
|
813 |
|
814 |
def _CheckInstanceConsole(self, instance, (status, consdata)): |
815 |
if instance.name == "inst7": |
816 |
self.assertEqual(status, constants.RS_NORMAL)
|
817 |
console = objects.InstanceConsole.FromDict(consdata) |
818 |
self.assertTrue(console.Validate())
|
819 |
self.assertEqual(console.host, instance.primary_node)
|
820 |
else:
|
821 |
self.assertEqual(status, constants.RS_UNAVAIL)
|
822 |
|
823 |
|
824 |
class TestGroupQuery(unittest.TestCase): |
825 |
|
826 |
def setUp(self): |
827 |
self.groups = [
|
828 |
objects.NodeGroup(name="default",
|
829 |
uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
|
830 |
alloc_policy=constants.ALLOC_POLICY_PREFERRED), |
831 |
objects.NodeGroup(name="restricted",
|
832 |
uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
|
833 |
alloc_policy=constants.ALLOC_POLICY_LAST_RESORT), |
834 |
] |
835 |
|
836 |
def _Create(self, selected): |
837 |
return query.Query(query.GROUP_FIELDS, selected)
|
838 |
|
839 |
def testSimple(self): |
840 |
q = self._Create(["name", "uuid", "alloc_policy"]) |
841 |
gqd = query.GroupQueryData(self.groups, None, None) |
842 |
|
843 |
self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG])) |
844 |
|
845 |
self.assertEqual(q.Query(gqd),
|
846 |
[[(constants.RS_NORMAL, "default"),
|
847 |
(constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
|
848 |
(constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED) |
849 |
], |
850 |
[(constants.RS_NORMAL, "restricted"),
|
851 |
(constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
|
852 |
(constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT) |
853 |
], |
854 |
]) |
855 |
|
856 |
def testNodes(self): |
857 |
groups_to_nodes = { |
858 |
"c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"], |
859 |
"d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"], |
860 |
} |
861 |
|
862 |
q = self._Create(["name", "node_cnt", "node_list"]) |
863 |
gqd = query.GroupQueryData(self.groups, groups_to_nodes, None) |
864 |
|
865 |
self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE])) |
866 |
|
867 |
self.assertEqual(q.Query(gqd),
|
868 |
[[(constants.RS_NORMAL, "default"),
|
869 |
(constants.RS_NORMAL, 2),
|
870 |
(constants.RS_NORMAL, ["node1", "node2"]), |
871 |
], |
872 |
[(constants.RS_NORMAL, "restricted"),
|
873 |
(constants.RS_NORMAL, 3),
|
874 |
(constants.RS_NORMAL, ["node1", "node9", "node10"]), |
875 |
], |
876 |
]) |
877 |
|
878 |
def testInstances(self): |
879 |
groups_to_instances = { |
880 |
"c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"], |
881 |
"d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"], |
882 |
} |
883 |
|
884 |
q = self._Create(["pinst_cnt", "pinst_list"]) |
885 |
gqd = query.GroupQueryData(self.groups, None, groups_to_instances) |
886 |
|
887 |
self.assertEqual(q.RequestedData(), set([query.GQ_INST])) |
888 |
|
889 |
self.assertEqual(q.Query(gqd),
|
890 |
[[(constants.RS_NORMAL, 2),
|
891 |
(constants.RS_NORMAL, ["inst1", "inst2"]), |
892 |
], |
893 |
[(constants.RS_NORMAL, 3),
|
894 |
(constants.RS_NORMAL, ["inst1", "inst9", "inst10"]), |
895 |
], |
896 |
]) |
897 |
|
898 |
|
899 |
class TestQueryFields(unittest.TestCase): |
900 |
def testAllFields(self): |
901 |
for fielddefs in query.ALL_FIELD_LISTS: |
902 |
result = query.QueryFields(fielddefs, None)
|
903 |
self.assert_(isinstance(result, dict)) |
904 |
response = objects.QueryFieldsResponse.FromDict(result) |
905 |
self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields], |
906 |
[(fdef2.name, fdef2.title) |
907 |
for (fdef2, _, _) in utils.NiceSort(fielddefs.values(), |
908 |
key=lambda x: x[0].name)]) |
909 |
|
910 |
def testSomeFields(self): |
911 |
rnd = random.Random(5357)
|
912 |
|
913 |
for _ in range(10): |
914 |
for fielddefs in query.ALL_FIELD_LISTS: |
915 |
if len(fielddefs) > 20: |
916 |
sample_size = rnd.randint(5, 20) |
917 |
else:
|
918 |
sample_size = rnd.randint(1, max(1, len(fielddefs) - 1)) |
919 |
fields = [fdef for (fdef, _, _) in rnd.sample(fielddefs.values(), |
920 |
sample_size)] |
921 |
result = query.QueryFields(fielddefs, [fdef.name for fdef in fields]) |
922 |
self.assert_(isinstance(result, dict)) |
923 |
response = objects.QueryFieldsResponse.FromDict(result) |
924 |
self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields], |
925 |
[(fdef2.name, fdef2.title) for fdef2 in fields]) |
926 |
|
927 |
|
928 |
if __name__ == "__main__": |
929 |
testutils.GanetiTestProgram() |