Commit d1c2dd75 authored by Iustin Pop's avatar Iustin Pop
Browse files

Move all iallocator functions into a class

This patch moves all the iallocator function into a separate class that
is then somewhat easier to use. It doesn't bring any new functionality.

The patch also changes the way the iallocator is called - the
OpTestAllocator opcode is no longer needed, and all its parameters
should be passed directly to the IAllocator constructor.

Reviewed-by: ultrotter
parent a424ce50
......@@ -3138,47 +3138,42 @@ class LUCreateInstance(LogicalUnit):
"""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,
ial = IAllocator(self.cfg, self.sstore,
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)
nics=nics,
mode=constants.IALLOCATOR_MODE_ALLOC)
_IAllocatorAddNewInstance(al_data, op)
ial.Run(self.op.iallocator)
text = serializer.Dump(al_data)
result = _IAllocatorRun(self.op.iallocator, text)
result = _IAllocatorValidateResult(result)
if not result["success"]:
if not ial.success:
raise errors.OpPrereqError("Can't compute nodes using"
" iallocator '%s': %s" % (self.op.iallocator,
result["info"]))
ial.info))
req_nodes = 1
if self.op.disk_template in constants.DTS_NET_MIRROR:
req_nodes += 1
if len(result["nodes"]) != req_nodes:
if len(ial.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]
(len(ial.nodes), req_nodes))
self.op.pnode = ial.nodes[0]
logger.ToStdout("Selected nodes for the instance: %s" %
(", ".join(result["nodes"]),))
(", ".join(ial.nodes),))
logger.Info("Selected nodes for instance %s via iallocator %s: %s" %
(self.op.instance_name, self.op.iallocator, result["nodes"]))
(self.op.instance_name, self.op.iallocator, ial.nodes))
if req_nodes == 2:
self.op.snode = result["nodes"][1]
self.op.snode = ial.nodes[1]
def BuildHooksEnv(self):
"""Build hooks env.
......@@ -4719,16 +4714,58 @@ class LUTestDelay(NoHooksLU):
" result: %s" % (node, node_result))
def _IAllocatorGetClusterData(cfg, sstore):
class IAllocator(object):
"""IAllocator framework.
An IAllocator instance has three sets of attributes:
- cfg/sstore that are needed to query the cluster
- input data (all members of the _KEYS class attribute are required)
- four buffer attributes (in|out_data|text), that represent the
input (to the external script) in text and data structure format,
and the output from it, again in two formats
- the result variables from the script (success, info, nodes) for
easy usage
"""
_KEYS = [
"mode", "name",
"mem_size", "disks", "disk_template",
"os", "tags", "nics", "vcpus",
]
def __init__(self, cfg, sstore, **kwargs):
self.cfg = cfg
self.sstore = sstore
# init buffer variables
self.in_text = self.out_text = self.in_data = self.out_data = None
# init all input fields so that pylint is happy
self.mode = self.name = None
self.mem_size = self.disks = self.disk_template = None
self.os = self.tags = self.nics = self.vcpus = None
# init result fields
self.success = self.info = self.nodes = None
for key in kwargs:
if key not in self._KEYS:
raise errors.ProgrammerError("Invalid input parameter '%s' to"
" IAllocator" % key)
setattr(self, key, kwargs[key])
for key in self._KEYS:
if key not in kwargs:
raise errors.ProgrammerError("Missing input parameter '%s' to"
" IAllocator" % key)
self._BuildInputData()
def _ComputeClusterData(self):
"""Compute the generic allocator input data.
This is the data that is independent of the actual operation.
"""
cfg = self.cfg
# cluster data
data = {
"version": 1,
"cluster_name": sstore.GetClusterName(),
"cluster_name": self.sstore.GetClusterName(),
"cluster_tags": list(cfg.GetClusterInfo().GetTags()),
# we don't have job IDs
}
......@@ -4786,10 +4823,9 @@ def _IAllocatorGetClusterData(cfg, sstore):
data["instances"] = instance_data
return data
self.in_data = data
def _IAllocatorAddNewInstance(data, op):
def _AddNewInstance(self):
"""Add new instance data to allocator structure.
This in combination with _AllocatorGetClusterData will create the
......@@ -4799,28 +4835,28 @@ def _IAllocatorAddNewInstance(data, op):
done.
"""
if len(op.disks) != 2:
data = self.in_data
if len(self.disks) != 2:
raise errors.OpExecError("Only two-disk configurations supported")
disk_space = _ComputeDiskSize(op.disk_template,
op.disks[0]["size"], op.disks[1]["size"])
disk_space = _ComputeDiskSize(self.disk_template,
self.disks[0]["size"], self.disks[1]["size"])
request = {
"type": "allocate",
"name": op.name,
"disk_template": op.disk_template,
"tags": op.tags,
"os": op.os,
"vcpus": op.vcpus,
"memory": op.mem_size,
"disks": op.disks,
"name": self.name,
"disk_template": self.disk_template,
"tags": self.tags,
"os": self.os,
"vcpus": self.vcpus,
"memory": self.mem_size,
"disks": self.disks,
"disk_space_total": disk_space,
"nics": op.nics,
"nics": self.nics,
}
data["request"] = request
def _IAllocatorAddRelocateInstance(data, op):
def _AddRelocateInstance(self):
"""Add relocate instance data to allocator structure.
This in combination with _IAllocatorGetClusterData will create the
......@@ -4830,17 +4866,32 @@ def _IAllocatorAddRelocateInstance(data, op):
done.
"""
data = self.in_data
request = {
"type": "replace_secondary",
"name": op.name,
"name": self.name,
}
data["request"] = request
def _BuildInputData(self):
"""Build input data structures.
def _IAllocatorRun(name, data):
"""
self._ComputeClusterData()
if self.mode == constants.IALLOCATOR_MODE_ALLOC:
self._AddNewInstance()
else:
self._AddRelocateInstance()
self.in_text = serializer.Dump(self.in_data)
def Run(self, name, validate=True):
"""Run an instance allocator and return the results.
"""
data = self.in_text
alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
os.path.isfile)
if alloc_script is None:
......@@ -4857,15 +4908,19 @@ def _IAllocatorRun(name, data):
(result.fail_reason, result.stdout))
finally:
os.unlink(fin_name)
return result.stdout
self.out_text = result.stdout
if validate:
self._ValidateResult()
def _IAllocatorValidateResult(data):
def _ValidateResult(self):
"""Process the allocator results.
This will process and if successful save the result in
self.out_data and the other parameters.
"""
try:
rdict = serializer.Load(data)
rdict = serializer.Load(self.out_text)
except Exception, err:
raise errors.OpExecError("Can't parse iallocator results: %s" % str(err))
......@@ -4876,11 +4931,12 @@ def _IAllocatorValidateResult(data):
if key not in rdict:
raise errors.OpExecError("Can't parse iallocator results:"
" missing key '%s'" % key)
setattr(self, key, rdict[key])
if not isinstance(rdict["nodes"], list):
raise errors.OpExecError("Can't parse iallocator results: 'nodes' key"
" is not a list")
return rdict
self.out_data = rdict
class LUTestAllocator(NoHooksLU):
......@@ -4951,15 +5007,21 @@ class LUTestAllocator(NoHooksLU):
"""Run the allocator test.
"""
data = _IAllocatorGetClusterData(self.cfg, self.sstore)
if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
_IAllocatorAddNewInstance(data, self.op)
else:
_IAllocatorAddRelocateInstance(data, self.op)
ial = IAllocator(self.cfg, self.sstore,
mode=self.op.mode,
name=self.op.name,
mem_size=self.op.mem_size,
disks=self.op.disks,
disk_template=self.op.disk_template,
os=self.op.os,
tags=self.op.tags,
nics=self.op.nics,
vcpus=self.op.vcpus,
)
text = serializer.Dump(data)
if self.op.direction == constants.IALLOCATOR_DIR_IN:
result = text
result = ial.in_text
else:
result = _IAllocatorRun(self.op.allocator, text)
ial.Run(self.op.allocator, validate=False)
result = ial.out_text
return result
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