Commit 298fe380 authored by Iustin Pop's avatar Iustin Pop
Browse files

Implement 'out' direction on allocator tests

This patch adds the paths for searching for instance allocators and
makes the LUTestAllocator code run the allocator and return the results
if the direction specified is 'out'. 'out' means that the opcode will
return the result of the allocator run, instead of the allocator input
file ('in').

The patch unifies all names to refer to 'iallocator' instead of plain
allocator.

The patch also adds an example allocator that can be used for testing
this new functionality.

Reviewed-by: ultrotter
parent d61df03e
......@@ -50,6 +50,17 @@ AC_ARG_WITH([os-search-path],
[os_search_path="'/srv/ganeti/os'"])
AC_SUBST(OS_SEARCH_PATH, $os_search_path)
# --with-iallocator-search-path=...
# do a bit of black sed magic to for quoting of the strings in the list
AC_ARG_WITH([iallocator-search-path],
[AS_HELP_STRING([--with-iallocator-search-path=LIST],
[comma separated list of directories to]
[ search for instance allocators (default is $libdir/ganeti/iallocators)]
)],
[iallocator_search_path=`echo -n "$withval" | sed -e "s/\([[^,]]*\)/'\1'/g"`],
[iallocator_search_path="'$libdir/$PACKAGE_NAME/iallocators'"])
AC_SUBST(IALLOCATOR_SEARCH_PATH, $iallocator_search_path)
# --with-xen-kernel=...
AC_ARG_WITH([xen-kernel],
[AS_HELP_STRING([--with-xen-kernel=PATH],
......
#!/usr/bin/python
#
# Copyright (C) 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Simple first-fit allocator for ganeti instance allocation framework.
This allocator just iterates over the nodes and selects the first one
that fits in both memory and disk space, without any consideration for
equal spread or VCPU oversubscription.
"""
import simplejson
import sys
def SelectNode(nodes, request, to_skip):
"""Select a node for the given instance
"""
disk_size = request["disk_space_total"]
selected = None
for nname, ninfo in nodes.iteritems():
if nname in to_skip:
continue
if request["memory"] > ninfo["free_memory"]:
continue
if disk_size > ninfo["free_disk"]:
continue
selected = nname
break
return selected
def OutputError(text):
"""Builds an error response with a given info message.
"""
error = {
"success": False,
"info": text,
}
print simplejson.dumps(error, indent=2)
return 1
def main():
"""Main function.
"""
if len(sys.argv) < 2:
print >> sys.stderr, "Usage: %s cluster.json" % (sys.argv[0])
return 1
data = simplejson.load(open(sys.argv[1]))
nodes = data["nodes"]
request = data["request"]
req_type = request["type"]
if req_type != "allocate":
print >> sys.stderr, "Unsupported allocator mode '%s'" % req_type
return 1
npri = SelectNode(nodes, request, [])
if npri is None:
return OutputError("Can't find a suitable primary node")
result_nodes = [npri]
if request["disk_template"] == "drbd":
nsec = SelectNode(nodes, request, result_nodes)
if nsec is None:
return OutputError("Can't find a suitable secondary node (%s selected"
" as primary)" % npri)
result_nodes.append(nsec)
result = {
"success": True,
"info": "Allocation successful",
"nodes": result_nodes,
}
print simplejson.dumps(result, indent=2)
return 0
if __name__ == "__main__":
sys.exit(main())
......@@ -25,6 +25,7 @@ _autoconf.py: Makefile
echo "XEN_KERNEL = '$(XEN_KERNEL)'"; \
echo "XEN_INITRD = '$(XEN_INITRD)'"; \
echo "FILE_STORAGE_DIR = '$(FILE_STORAGE_DIR)'"; \
echo "IALLOCATOR_SEARCH_PATH = [$(IALLOCATOR_SEARCH_PATH)]"; \
} > $@
pre-check: all
......
......@@ -4650,7 +4650,7 @@ class LUTestDelay(NoHooksLU):
" result: %s" % (node, node_result))
def _AllocatorGetClusterData(cfg, sstore):
def _IAllocatorGetClusterData(cfg, sstore):
"""Compute the generic allocator input data.
This is the data that is independent of the actual operation.
......@@ -4720,7 +4720,7 @@ def _AllocatorGetClusterData(cfg, sstore):
return data
def _AllocatorAddNewInstance(data, op):
def _IAllocatorAddNewInstance(data, op):
"""Add new instance data to allocator structure.
This in combination with _AllocatorGetClusterData will create the
......@@ -4730,6 +4730,12 @@ def _AllocatorAddNewInstance(data, op):
done.
"""
if len(op.disks) != 2:
raise errors.OpExecError("Only two-disk configurations supported")
disk_space = _ComputeDiskSize(op.disk_template,
op.disks[0]["size"], op.disks[1]["size"])
request = {
"type": "allocate",
"name": op.name,
......@@ -4739,15 +4745,16 @@ def _AllocatorAddNewInstance(data, op):
"vcpus": op.vcpus,
"memory": op.mem_size,
"disks": op.disks,
"disk_space_total": disk_space,
"nics": op.nics,
}
data["request"] = request
def _AllocatorAddRelocateInstance(data, op):
def _IAllocatorAddRelocateInstance(data, op):
"""Add relocate instance data to allocator structure.
This in combination with _AllocatorGetClusterData will create the
This in combination with _IAllocatorGetClusterData will create the
correct structure needed as input for the allocator.
The checks for the completeness of the opcode must have already been
......@@ -4761,6 +4768,29 @@ def _AllocatorAddRelocateInstance(data, op):
data["request"] = request
def _IAllocatorRun(name, data):
"""Run an instance allocator and return the results.
"""
alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
os.path.isfile)
if alloc_script is None:
raise errors.OpExecError("Can't find allocator")
fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
try:
os.write(fd, data)
os.close(fd)
result = utils.RunCmd([alloc_script, fin_name])
if result.failed:
raise errors.OpExecError("Instance allocator call failed: %s,"
" output: %s" %
(result.fail_reason, result.stdout))
finally:
os.unlink(fin_name)
return result.stdout
class LUTestAllocator(NoHooksLU):
"""Run allocator tests.
......@@ -4775,7 +4805,7 @@ class LUTestAllocator(NoHooksLU):
This checks the opcode parameters depending on the director and mode test.
"""
if self.op.mode == constants.ALF_MODE_ALLOC:
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
for attr in ["name", "mem_size", "disks", "disk_template",
"os", "tags", "nics", "vcpus"]:
if not hasattr(self.op, attr):
......@@ -4796,6 +4826,8 @@ class LUTestAllocator(NoHooksLU):
" 'nics' parameter")
if not isinstance(self.op.disks, list):
raise errors.OpPrereqError("Invalid parameter 'disks'")
if len(self.op.disks) != 2:
raise errors.OpPrereqError("Only two-disk configurations supported")
for row in self.op.disks:
if (not isinstance(row, dict) or
"size" not in row or
......@@ -4804,7 +4836,7 @@ class LUTestAllocator(NoHooksLU):
row["mode"] not in ['r', 'w']):
raise errors.OpPrereqError("Invalid contents of the"
" 'disks' parameter")
elif self.op.mode == constants.ALF_MODE_RELOC:
elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
if not hasattr(self.op, "name"):
raise errors.OpPrereqError("Missing attribute 'name' on opcode input")
fname = self.cfg.ExpandInstanceName(self.op.name)
......@@ -4816,11 +4848,10 @@ class LUTestAllocator(NoHooksLU):
raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
self.op.mode)
if self.op.direction == constants.ALF_DIR_OUT:
if not hasattr(self.op, "allocator"):
if self.op.direction == constants.IALLOCATOR_DIR_OUT:
if not hasattr(self.op, "allocator") or self.op.allocator is None:
raise errors.OpPrereqError("Missing allocator name")
raise errors.OpPrereqError("Allocator out mode not supported yet")
elif self.op.direction != constants.ALF_DIR_IN:
elif self.op.direction != constants.IALLOCATOR_DIR_IN:
raise errors.OpPrereqError("Wrong allocator test '%s'" %
self.op.direction)
......@@ -4828,14 +4859,18 @@ class LUTestAllocator(NoHooksLU):
"""Run the allocator test.
"""
data = _AllocatorGetClusterData(self.cfg, self.sstore)
if self.op.mode == constants.ALF_MODE_ALLOC:
_AllocatorAddNewInstance(data, self.op)
data = _IAllocatorGetClusterData(self.cfg, self.sstore)
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
_IAllocatorAddNewInstance(data, self.op)
else:
_AllocatorAddRelocateInstance(data, self.op)
_IAllocatorAddRelocateInstance(data, self.op)
if _JSON_INDENT is None:
text = simplejson.dumps(data)
else:
text = simplejson.dumps(data, indent=_JSON_INDENT)
return text
if self.op.direction == constants.IALLOCATOR_DIR_IN:
result = text
else:
result = _IAllocatorRun(self.op.allocator, text)
return result
......@@ -185,7 +185,8 @@ VERIFY_NPLUSONE_MEM = 'nplusone_mem'
VERIFY_OPTIONAL_CHECKS = frozenset([VERIFY_NPLUSONE_MEM])
# Allocator framework constants
ALF_DIR_IN = "in"
ALF_DIR_OUT = "out"
ALF_MODE_ALLOC = "allocate"
ALF_MODE_RELOC = "relocate"
IALLOCATOR_DIR_IN = "in"
IALLOCATOR_DIR_OUT = "out"
IALLOCATOR_MODE_ALLOC = "allocate"
IALLOCATOR_MODE_RELOC = "relocate"
IALLOCATOR_SEARCH_PATH = _autoconf.IALLOCATOR_SEARCH_PATH
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment