--- /dev/null
+#!/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())
" 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.
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
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,
"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
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.
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):
" '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
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)
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)
"""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