Revision 298fe380
b/configure.ac | ||
---|---|---|
50 | 50 |
[os_search_path="'/srv/ganeti/os'"]) |
51 | 51 |
AC_SUBST(OS_SEARCH_PATH, $os_search_path) |
52 | 52 |
|
53 |
# --with-iallocator-search-path=... |
|
54 |
# do a bit of black sed magic to for quoting of the strings in the list |
|
55 |
AC_ARG_WITH([iallocator-search-path], |
|
56 |
[AS_HELP_STRING([--with-iallocator-search-path=LIST], |
|
57 |
[comma separated list of directories to] |
|
58 |
[ search for instance allocators (default is $libdir/ganeti/iallocators)] |
|
59 |
)], |
|
60 |
[iallocator_search_path=`echo -n "$withval" | sed -e "s/\([[^,]]*\)/'\1'/g"`], |
|
61 |
[iallocator_search_path="'$libdir/$PACKAGE_NAME/iallocators'"]) |
|
62 |
AC_SUBST(IALLOCATOR_SEARCH_PATH, $iallocator_search_path) |
|
63 |
|
|
53 | 64 |
# --with-xen-kernel=... |
54 | 65 |
AC_ARG_WITH([xen-kernel], |
55 | 66 |
[AS_HELP_STRING([--with-xen-kernel=PATH], |
b/doc/examples/dumb-allocator | ||
---|---|---|
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 |
import simplejson |
|
29 |
import sys |
|
30 |
|
|
31 |
|
|
32 |
def SelectNode(nodes, request, to_skip): |
|
33 |
"""Select a node for the given instance |
|
34 |
|
|
35 |
""" |
|
36 |
disk_size = request["disk_space_total"] |
|
37 |
selected = None |
|
38 |
for nname, ninfo in nodes.iteritems(): |
|
39 |
if nname in to_skip: |
|
40 |
continue |
|
41 |
if request["memory"] > ninfo["free_memory"]: |
|
42 |
continue |
|
43 |
if disk_size > ninfo["free_disk"]: |
|
44 |
continue |
|
45 |
selected = nname |
|
46 |
break |
|
47 |
return selected |
|
48 |
|
|
49 |
|
|
50 |
def OutputError(text): |
|
51 |
"""Builds an error response with a given info message. |
|
52 |
|
|
53 |
""" |
|
54 |
error = { |
|
55 |
"success": False, |
|
56 |
"info": text, |
|
57 |
} |
|
58 |
print simplejson.dumps(error, indent=2) |
|
59 |
return 1 |
|
60 |
|
|
61 |
|
|
62 |
def main(): |
|
63 |
"""Main function. |
|
64 |
|
|
65 |
""" |
|
66 |
if len(sys.argv) < 2: |
|
67 |
print >> sys.stderr, "Usage: %s cluster.json" % (sys.argv[0]) |
|
68 |
return 1 |
|
69 |
|
|
70 |
data = simplejson.load(open(sys.argv[1])) |
|
71 |
|
|
72 |
nodes = data["nodes"] |
|
73 |
request = data["request"] |
|
74 |
req_type = request["type"] |
|
75 |
if req_type != "allocate": |
|
76 |
print >> sys.stderr, "Unsupported allocator mode '%s'" % req_type |
|
77 |
return 1 |
|
78 |
|
|
79 |
npri = SelectNode(nodes, request, []) |
|
80 |
if npri is None: |
|
81 |
return OutputError("Can't find a suitable primary node") |
|
82 |
|
|
83 |
result_nodes = [npri] |
|
84 |
if request["disk_template"] == "drbd": |
|
85 |
nsec = SelectNode(nodes, request, result_nodes) |
|
86 |
if nsec is None: |
|
87 |
return OutputError("Can't find a suitable secondary node (%s selected" |
|
88 |
" as primary)" % npri) |
|
89 |
result_nodes.append(nsec) |
|
90 |
|
|
91 |
result = { |
|
92 |
"success": True, |
|
93 |
"info": "Allocation successful", |
|
94 |
"nodes": result_nodes, |
|
95 |
} |
|
96 |
print simplejson.dumps(result, indent=2) |
|
97 |
return 0 |
|
98 |
|
|
99 |
if __name__ == "__main__": |
|
100 |
sys.exit(main()) |
b/lib/Makefile.am | ||
---|---|---|
25 | 25 |
echo "XEN_KERNEL = '$(XEN_KERNEL)'"; \ |
26 | 26 |
echo "XEN_INITRD = '$(XEN_INITRD)'"; \ |
27 | 27 |
echo "FILE_STORAGE_DIR = '$(FILE_STORAGE_DIR)'"; \ |
28 |
echo "IALLOCATOR_SEARCH_PATH = [$(IALLOCATOR_SEARCH_PATH)]"; \ |
|
28 | 29 |
} > $@ |
29 | 30 |
|
30 | 31 |
pre-check: all |
b/lib/cmdlib.py | ||
---|---|---|
4650 | 4650 |
" result: %s" % (node, node_result)) |
4651 | 4651 |
|
4652 | 4652 |
|
4653 |
def _AllocatorGetClusterData(cfg, sstore): |
|
4653 |
def _IAllocatorGetClusterData(cfg, sstore):
|
|
4654 | 4654 |
"""Compute the generic allocator input data. |
4655 | 4655 |
|
4656 | 4656 |
This is the data that is independent of the actual operation. |
... | ... | |
4720 | 4720 |
return data |
4721 | 4721 |
|
4722 | 4722 |
|
4723 |
def _AllocatorAddNewInstance(data, op): |
|
4723 |
def _IAllocatorAddNewInstance(data, op):
|
|
4724 | 4724 |
"""Add new instance data to allocator structure. |
4725 | 4725 |
|
4726 | 4726 |
This in combination with _AllocatorGetClusterData will create the |
... | ... | |
4730 | 4730 |
done. |
4731 | 4731 |
|
4732 | 4732 |
""" |
4733 |
if len(op.disks) != 2: |
|
4734 |
raise errors.OpExecError("Only two-disk configurations supported") |
|
4735 |
|
|
4736 |
disk_space = _ComputeDiskSize(op.disk_template, |
|
4737 |
op.disks[0]["size"], op.disks[1]["size"]) |
|
4738 |
|
|
4733 | 4739 |
request = { |
4734 | 4740 |
"type": "allocate", |
4735 | 4741 |
"name": op.name, |
... | ... | |
4739 | 4745 |
"vcpus": op.vcpus, |
4740 | 4746 |
"memory": op.mem_size, |
4741 | 4747 |
"disks": op.disks, |
4748 |
"disk_space_total": disk_space, |
|
4742 | 4749 |
"nics": op.nics, |
4743 | 4750 |
} |
4744 | 4751 |
data["request"] = request |
4745 | 4752 |
|
4746 | 4753 |
|
4747 |
def _AllocatorAddRelocateInstance(data, op): |
|
4754 |
def _IAllocatorAddRelocateInstance(data, op):
|
|
4748 | 4755 |
"""Add relocate instance data to allocator structure. |
4749 | 4756 |
|
4750 |
This in combination with _AllocatorGetClusterData will create the |
|
4757 |
This in combination with _IAllocatorGetClusterData will create the
|
|
4751 | 4758 |
correct structure needed as input for the allocator. |
4752 | 4759 |
|
4753 | 4760 |
The checks for the completeness of the opcode must have already been |
... | ... | |
4761 | 4768 |
data["request"] = request |
4762 | 4769 |
|
4763 | 4770 |
|
4771 |
def _IAllocatorRun(name, data): |
|
4772 |
"""Run an instance allocator and return the results. |
|
4773 |
|
|
4774 |
""" |
|
4775 |
alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH, |
|
4776 |
os.path.isfile) |
|
4777 |
if alloc_script is None: |
|
4778 |
raise errors.OpExecError("Can't find allocator") |
|
4779 |
|
|
4780 |
fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.") |
|
4781 |
try: |
|
4782 |
os.write(fd, data) |
|
4783 |
os.close(fd) |
|
4784 |
result = utils.RunCmd([alloc_script, fin_name]) |
|
4785 |
if result.failed: |
|
4786 |
raise errors.OpExecError("Instance allocator call failed: %s," |
|
4787 |
" output: %s" % |
|
4788 |
(result.fail_reason, result.stdout)) |
|
4789 |
finally: |
|
4790 |
os.unlink(fin_name) |
|
4791 |
return result.stdout |
|
4792 |
|
|
4793 |
|
|
4764 | 4794 |
class LUTestAllocator(NoHooksLU): |
4765 | 4795 |
"""Run allocator tests. |
4766 | 4796 |
|
... | ... | |
4775 | 4805 |
This checks the opcode parameters depending on the director and mode test. |
4776 | 4806 |
|
4777 | 4807 |
""" |
4778 |
if self.op.mode == constants.ALF_MODE_ALLOC:
|
|
4808 |
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
|
|
4779 | 4809 |
for attr in ["name", "mem_size", "disks", "disk_template", |
4780 | 4810 |
"os", "tags", "nics", "vcpus"]: |
4781 | 4811 |
if not hasattr(self.op, attr): |
... | ... | |
4796 | 4826 |
" 'nics' parameter") |
4797 | 4827 |
if not isinstance(self.op.disks, list): |
4798 | 4828 |
raise errors.OpPrereqError("Invalid parameter 'disks'") |
4829 |
if len(self.op.disks) != 2: |
|
4830 |
raise errors.OpPrereqError("Only two-disk configurations supported") |
|
4799 | 4831 |
for row in self.op.disks: |
4800 | 4832 |
if (not isinstance(row, dict) or |
4801 | 4833 |
"size" not in row or |
... | ... | |
4804 | 4836 |
row["mode"] not in ['r', 'w']): |
4805 | 4837 |
raise errors.OpPrereqError("Invalid contents of the" |
4806 | 4838 |
" 'disks' parameter") |
4807 |
elif self.op.mode == constants.ALF_MODE_RELOC:
|
|
4839 |
elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
|
|
4808 | 4840 |
if not hasattr(self.op, "name"): |
4809 | 4841 |
raise errors.OpPrereqError("Missing attribute 'name' on opcode input") |
4810 | 4842 |
fname = self.cfg.ExpandInstanceName(self.op.name) |
... | ... | |
4816 | 4848 |
raise errors.OpPrereqError("Invalid test allocator mode '%s'" % |
4817 | 4849 |
self.op.mode) |
4818 | 4850 |
|
4819 |
if self.op.direction == constants.ALF_DIR_OUT:
|
|
4820 |
if not hasattr(self.op, "allocator"): |
|
4851 |
if self.op.direction == constants.IALLOCATOR_DIR_OUT:
|
|
4852 |
if not hasattr(self.op, "allocator") or self.op.allocator is None:
|
|
4821 | 4853 |
raise errors.OpPrereqError("Missing allocator name") |
4822 |
raise errors.OpPrereqError("Allocator out mode not supported yet") |
|
4823 |
elif self.op.direction != constants.ALF_DIR_IN: |
|
4854 |
elif self.op.direction != constants.IALLOCATOR_DIR_IN: |
|
4824 | 4855 |
raise errors.OpPrereqError("Wrong allocator test '%s'" % |
4825 | 4856 |
self.op.direction) |
4826 | 4857 |
|
... | ... | |
4828 | 4859 |
"""Run the allocator test. |
4829 | 4860 |
|
4830 | 4861 |
""" |
4831 |
data = _AllocatorGetClusterData(self.cfg, self.sstore) |
|
4832 |
if self.op.mode == constants.ALF_MODE_ALLOC:
|
|
4833 |
_AllocatorAddNewInstance(data, self.op) |
|
4862 |
data = _IAllocatorGetClusterData(self.cfg, self.sstore)
|
|
4863 |
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
|
|
4864 |
_IAllocatorAddNewInstance(data, self.op)
|
|
4834 | 4865 |
else: |
4835 |
_AllocatorAddRelocateInstance(data, self.op) |
|
4866 |
_IAllocatorAddRelocateInstance(data, self.op)
|
|
4836 | 4867 |
|
4837 | 4868 |
if _JSON_INDENT is None: |
4838 | 4869 |
text = simplejson.dumps(data) |
4839 | 4870 |
else: |
4840 | 4871 |
text = simplejson.dumps(data, indent=_JSON_INDENT) |
4841 |
return text |
|
4872 |
if self.op.direction == constants.IALLOCATOR_DIR_IN: |
|
4873 |
result = text |
|
4874 |
else: |
|
4875 |
result = _IAllocatorRun(self.op.allocator, text) |
|
4876 |
return result |
b/lib/constants.py | ||
---|---|---|
185 | 185 |
VERIFY_OPTIONAL_CHECKS = frozenset([VERIFY_NPLUSONE_MEM]) |
186 | 186 |
|
187 | 187 |
# Allocator framework constants |
188 |
ALF_DIR_IN = "in" |
|
189 |
ALF_DIR_OUT = "out" |
|
190 |
ALF_MODE_ALLOC = "allocate" |
|
191 |
ALF_MODE_RELOC = "relocate" |
|
188 |
IALLOCATOR_DIR_IN = "in" |
|
189 |
IALLOCATOR_DIR_OUT = "out" |
|
190 |
IALLOCATOR_MODE_ALLOC = "allocate" |
|
191 |
IALLOCATOR_MODE_RELOC = "relocate" |
|
192 |
IALLOCATOR_SEARCH_PATH = _autoconf.IALLOCATOR_SEARCH_PATH |
Also available in: Unified diff