Commit 538475ca authored by Iustin Pop's avatar Iustin Pop
Browse files

IAllocator part 3: LUCreateInstance changes

This (final) patch allows the instance's nodes to be selected
automatically based on the passed allocator algorithm.

The patch changes the pnode opcode parameter from required to optional,
now either the pnode or the iallocator must be passed.

A possible improvement could be to organize all the _IAllocator
functions into a separate class, but that can come later and the current
version is functionally ok.

Reviewed-by: ultrotter
parent 901a65c1
......@@ -47,16 +47,17 @@ def SelectNode(nodes, request, to_skip):
return selected
def OutputError(text):
def OutputError(text, exit_code=1):
"""Builds an error response with a given info message.
"""
error = {
"success": False,
"info": text,
"nodes": [],
}
print simplejson.dumps(error, indent=2)
return 1
return exit_code
def main():
......@@ -78,14 +79,14 @@ def main():
npri = SelectNode(nodes, request, [])
if npri is None:
return OutputError("Can't find a suitable primary node")
return OutputError("Can't find a suitable primary node", exit_code=0)
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)
" as primary)" % npri, exit_code=0)
result_nodes.append(nsec)
result = {
......
......@@ -3126,10 +3126,59 @@ class LUCreateInstance(LogicalUnit):
"""
HPATH = "instance-add"
HTYPE = constants.HTYPE_INSTANCE
_OP_REQP = ["instance_name", "mem_size", "disk_size", "pnode",
_OP_REQP = ["instance_name", "mem_size", "disk_size",
"disk_template", "swap_size", "mode", "start", "vcpus",
"wait_for_sync", "ip_check", "mac"]
def _RunAllocator(self):
"""Run the allocator based on input opcode.
"""
al_data = _IAllocatorGetClusterData(self.cfg, self.sstore)
disks = [{"size": self.op.disk_size, "mode": "w"},
{"size": self.op.swap_size, "mode": "w"}]
nics = [{"mac": self.op.mac, "ip": getattr(self.op, "ip", None),
"bridge": self.op.bridge}]
op = opcodes.OpTestAllocator(name=self.op.instance_name,
disk_template=self.op.disk_template,
tags=[],
os=self.op.os_type,
vcpus=self.op.vcpus,
mem_size=self.op.mem_size,
disks=disks,
nics=nics)
_IAllocatorAddNewInstance(al_data, op)
if _JSON_INDENT is None:
text = simplejson.dumps(al_data)
else:
text = simplejson.dumps(al_data, indent=_JSON_INDENT)
result = _IAllocatorRun(self.op.iallocator, text)
result = _IAllocatorValidateResult(result)
if not result["success"]:
raise errors.OpPrereqError("Can't compute nodes using"
" iallocator '%s': %s" % (self.op.iallocator,
result["info"]))
req_nodes = 1
if self.op.disk_template in constants.DTS_NET_MIRROR:
req_nodes += 1
if len(result["nodes"]) != req_nodes:
raise errors.OpPrereqError("iallocator '%s' returned invalid number"
" of nodes (%s), required %s" %
(len(result["nodes"]), req_nodes))
self.op.pnode = result["nodes"][0]
logger.ToStdout("Selected nodes for the instance: %s" %
(", ".join(result["nodes"]),))
logger.Info("Selected nodes for instance %s via iallocator %s: %s" %
(self.op.instance_name, self.op.iallocator, result["nodes"]))
if req_nodes == 2:
self.op.snode = result["nodes"][1]
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -3166,7 +3215,9 @@ class LUCreateInstance(LogicalUnit):
"""Check prerequisites.
"""
for attr in ["kernel_path", "initrd_path", "hvm_boot_order"]:
# set optional parameters to none if they don't exist
for attr in ["kernel_path", "initrd_path", "hvm_boot_order", "pnode",
"iallocator"]:
if not hasattr(self.op, attr):
setattr(self.op, attr, None)
......@@ -3284,6 +3335,14 @@ class LUCreateInstance(LogicalUnit):
if self.op.file_storage_dir and os.path.isabs(self.op.file_storage_dir):
raise errors.OpPrereqError("File storage directory not a relative"
" path")
#### allocator run
if [self.op.iallocator, self.op.pnode].count(None) != 1:
raise errors.OpPrereqError("One and only one of iallocator and primary"
" node must be given")
if self.op.iallocator is not None:
self._RunAllocator()
#### node related checks
......@@ -4784,7 +4843,7 @@ def _IAllocatorRun(name, data):
alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
os.path.isfile)
if alloc_script is None:
raise errors.OpExecError("Can't find allocator")
raise errors.OpExecError("Can't find allocator '%s'" % name)
fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
try:
......@@ -4800,6 +4859,29 @@ def _IAllocatorRun(name, data):
return result.stdout
def _IAllocatorValidateResult(data):
"""Process the allocator results.
"""
try:
rdict = simplejson.loads(data)
except Exception, err:
raise errors.OpExecError("Can't parse iallocator results: %s" % str(err))
if not isinstance(rdict, dict):
raise errors.OpExecError("Can't parse iallocator results: not a dict")
for key in "success", "info", "nodes":
if key not in rdict:
raise errors.OpExecError("Can't parse iallocator results:"
" missing key '%s'" % key)
if not isinstance(rdict["nodes"], list):
raise errors.OpExecError("Can't parse iallocator results: 'nodes' key"
" is not a list")
return rdict
class LUTestAllocator(NoHooksLU):
"""Run allocator tests.
......
......@@ -284,6 +284,7 @@ class OpCreateInstance(OpCode):
"wait_for_sync", "ip_check", "mac",
"kernel_path", "initrd_path", "hvm_boot_order",
"file_storage_dir", "file_driver",
"iallocator",
]
......
......@@ -99,7 +99,8 @@ def ImportInstance(opts, args):
vcpus=opts.vcpus, ip_check=opts.ip_check,
ip=opts.ip, bridge=opts.bridge, start=False,
src_node=opts.src_node, src_path=opts.src_dir,
wait_for_sync=opts.wait_for_sync, mac="auto")
wait_for_sync=opts.wait_for_sync, mac="auto",
iallocator=opts.iallocator)
SubmitOpCode(op)
return 0
......@@ -137,6 +138,9 @@ import_opts = [
make_option("--no-ip-check", dest="ip_check", default=True,
action="store_false", help="Don't check that the instance's IP"
" is alive"),
make_option("--iallocator", metavar="<NAME>",
help="Select nodes for the instance automatically using the"
" <NAME> iallocator plugin", default=None, type="string"),
]
commands = {
......
......@@ -261,7 +261,8 @@ def AddInstance(opts, args):
initrd_path=initrd_path,
hvm_boot_order=opts.hvm_boot_order,
file_storage_dir = opts.file_storage_dir,
file_driver = opts.file_driver)
file_driver = opts.file_driver,
iallocator=opts.iallocator)
SubmitOpCode(op)
return 0
......@@ -760,14 +761,17 @@ add_opts = [
default=None,
type="string", metavar="<FILENAME>"),
make_option("--hvm-boot-order", dest="hvm_boot_order",
help="boot device order for HVM (one or more of [acdn])",
help="Boot device order for HVM (one or more of [acdn])",
default=None, type="string", metavar="<BOOTORDER>"),
make_option("--file-storage-dir", dest="file_storage_dir",
help="Relative path under default cluster-wide file storage dir"
" to store file-based disks", default=None,
metavar="<DIR>"),
make_option("--file-driver", dest="file_driver", help="Driver to use"
" for image files", default="loop", metavar="<DRIVER>")
" for image files", default="loop", metavar="<DRIVER>"),
make_option("--iallocator", metavar="<NAME>",
help="Select nodes for the instance automatically using the"
" <NAME> iallocator plugin", default=None, type="string"),
]
commands = {
......
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