Revision 6f547f96
b/Makefile.am | ||
---|---|---|
402 | 402 |
doc/examples/ganeti-kvm-poweroff.initd.in \ |
403 | 403 |
doc/examples/ganeti.cron.in \ |
404 | 404 |
doc/examples/gnt-config-backup.in \ |
405 |
doc/examples/dumb-allocator \ |
|
406 | 405 |
doc/examples/ganeti.default \ |
407 | 406 |
doc/examples/ganeti.default-debug \ |
408 | 407 |
doc/examples/hooks/ethers \ |
/dev/null | ||
---|---|---|
1 |
#!/usr/bin/python |
|
2 |
# |
|
3 |
|
|
4 |
# Copyright (C) 2008 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 |
"""Simple first-fit allocator for ganeti instance allocation framework. |
|
22 |
|
|
23 |
This allocator just iterates over the nodes and selects the first one |
|
24 |
that fits in both memory and disk space, without any consideration for |
|
25 |
equal spread or VCPU oversubscription. |
|
26 |
|
|
27 |
""" |
|
28 |
|
|
29 |
import simplejson |
|
30 |
import sys |
|
31 |
|
|
32 |
|
|
33 |
def SelectNode(nodes, request, to_skip): |
|
34 |
"""Select a node for the given instance |
|
35 |
|
|
36 |
""" |
|
37 |
disk_size = request["disk_space_total"] |
|
38 |
selected = None |
|
39 |
for nname, ninfo in nodes.iteritems(): |
|
40 |
if nname in to_skip: |
|
41 |
continue |
|
42 |
if request["memory"] > ninfo["free_memory"]: |
|
43 |
continue |
|
44 |
if disk_size > ninfo["free_disk"]: |
|
45 |
continue |
|
46 |
selected = nname |
|
47 |
break |
|
48 |
return selected |
|
49 |
|
|
50 |
|
|
51 |
def OutputError(text, exit_code=1): |
|
52 |
"""Builds an error response with a given info message. |
|
53 |
|
|
54 |
""" |
|
55 |
error = { |
|
56 |
"success": False, |
|
57 |
"info": text, |
|
58 |
"nodes": [], |
|
59 |
} |
|
60 |
print simplejson.dumps(error, indent=2) |
|
61 |
return exit_code |
|
62 |
|
|
63 |
|
|
64 |
def main(): |
|
65 |
"""Main function. |
|
66 |
|
|
67 |
""" |
|
68 |
if len(sys.argv) < 2: |
|
69 |
print >> sys.stderr, "Usage: %s cluster.json" % (sys.argv[0]) |
|
70 |
return 1 |
|
71 |
|
|
72 |
data = simplejson.load(open(sys.argv[1])) |
|
73 |
|
|
74 |
nodes = data["nodes"] |
|
75 |
request = data["request"] |
|
76 |
req_type = request["type"] |
|
77 |
offline_nodes = [name for name in nodes if nodes[name]["offline"]] |
|
78 |
drained_nodes = [name for name in nodes if nodes[name]["offline"]] |
|
79 |
if req_type == "allocate": |
|
80 |
forbidden_nodes = offline_nodes + drained_nodes |
|
81 |
inst_data = request |
|
82 |
elif req_type == "relocate": |
|
83 |
idict = data["instances"][request["name"]] |
|
84 |
forbidden_nodes = idict["nodes"] + offline_nodes + drained_nodes |
|
85 |
inst_data = idict |
|
86 |
inst_data["disk_space_total"] = request["disk_space_total"] |
|
87 |
else: |
|
88 |
return OutputError("Unsupported allocator mode '%s'" % req_type) |
|
89 |
|
|
90 |
result_nodes = [] |
|
91 |
while len(result_nodes) < request["required_nodes"]: |
|
92 |
new_selection = SelectNode(nodes, inst_data, result_nodes+forbidden_nodes) |
|
93 |
if new_selection is None: |
|
94 |
return OutputError("Can't find a suitable node for position %s" |
|
95 |
" (already selected: %s)" % |
|
96 |
(len(result_nodes) + 1, ", ".join(result_nodes)), |
|
97 |
exit_code=0) |
|
98 |
result_nodes.append(new_selection) |
|
99 |
|
|
100 |
result = { |
|
101 |
"success": True, |
|
102 |
"info": "Allocation successful", |
|
103 |
"nodes": result_nodes, |
|
104 |
} |
|
105 |
print simplejson.dumps(result, indent=2) |
|
106 |
return 0 |
|
107 |
|
|
108 |
|
|
109 |
if __name__ == "__main__": |
|
110 |
sys.exit(main()) |
b/doc/iallocator.rst | ||
---|---|---|
322 | 322 |
"disk_template": "drbd", |
323 | 323 |
"memory": 2048, |
324 | 324 |
"disk_space_total": 3328, |
325 |
"os": "etch-image"
|
|
325 |
"os": "debootstrap+default"
|
|
326 | 326 |
}, |
327 | 327 |
"cluster_name": "cluster1.example.com", |
328 | 328 |
"instances": { |
... | ... | |
352 | 352 |
"nodes": [ |
353 | 353 |
"nodee1.com" |
354 | 354 |
], |
355 |
"os": "etch-image"
|
|
355 |
"os": "debootstrap+default"
|
|
356 | 356 |
}, |
357 | 357 |
"instance2.example.com": { |
358 | 358 |
"tags": [], |
... | ... | |
381 | 381 |
"node2.example.com", |
382 | 382 |
"node3.example.com" |
383 | 383 |
], |
384 |
"os": "etch-image"
|
|
384 |
"os": "debootstrap+default"
|
|
385 | 385 |
} |
386 | 386 |
}, |
387 | 387 |
"version": 1, |
... | ... | |
483 | 483 |
~~~~~~~~~~~~~~~~~~~~~ |
484 | 484 |
:: |
485 | 485 |
|
486 |
# gnt-instance add -t plain -m 2g --os-size 1g --swap-size 512m --iallocator dumb-allocator -o etch-image instance3
|
|
486 |
# gnt-instance add -t plain -m 2g --os-size 1g --swap-size 512m --iallocator hail -o debootstrap+default instance3
|
|
487 | 487 |
Selected nodes for the instance: node1.example.com |
488 | 488 |
* creating instance disks... |
489 | 489 |
[...] |
490 | 490 |
|
491 |
# gnt-instance add -t plain -m 3400m --os-size 1g --swap-size 512m --iallocator dumb-allocator -o etch-image instance4
|
|
491 |
# gnt-instance add -t plain -m 3400m --os-size 1g --swap-size 512m --iallocator hail -o debootstrap+default instance4
|
|
492 | 492 |
Failure: prerequisites not met for this operation: |
493 |
Can't compute nodes using iallocator 'dumb-allocator': Can't find a suitable node for position 1 (already selected: )
|
|
493 |
Can't compute nodes using iallocator 'hail': Can't find a suitable node for position 1 (already selected: )
|
|
494 | 494 |
|
495 |
# gnt-instance add -t drbd -m 1400m --os-size 1g --swap-size 512m --iallocator dumb-allocator -o etch-image instance5
|
|
495 |
# gnt-instance add -t drbd -m 1400m --os-size 1g --swap-size 512m --iallocator hail -o debootstrap+default instance5
|
|
496 | 496 |
Failure: prerequisites not met for this operation: |
497 |
Can't compute nodes using iallocator 'dumb-allocator': Can't find a suitable node for position 2 (already selected: node1.example.com) |
|
497 |
Can't compute nodes using iallocator 'hail': Can't find a suitable node for position 2 (already selected: node1.example.com) |
|
498 |
|
|
499 |
Reference implementation |
|
500 |
~~~~~~~~~~~~~~~~~~~~~~~~ |
|
501 |
|
|
502 |
Ganeti's default iallocator is "hail" which is part of the separate |
|
503 |
ganeti-htools project. In order to see its source code please clone |
|
504 |
``git://git.ganeti.org/htools.git``. Note that htools is implemented |
|
505 |
using the Haskell programming language. |
|
498 | 506 |
|
499 | 507 |
.. vim: set textwidth=72 : |
500 | 508 |
.. Local Variables: |
Also available in: Unified diff