Statistics
| Branch: | Tag: | Revision:

root / lib / masterd / iallocator.py @ 1d4a4b26

History | View | Annotate | Download (20.6 kB)

1 0fcd0cad René Nussbaumer
#
2 0fcd0cad René Nussbaumer
#
3 0fcd0cad René Nussbaumer
4 473d87a3 Iustin Pop
# Copyright (C) 2012, 2013 Google Inc.
5 0fcd0cad René Nussbaumer
#
6 0fcd0cad René Nussbaumer
# This program is free software; you can redistribute it and/or modify
7 0fcd0cad René Nussbaumer
# it under the terms of the GNU General Public License as published by
8 0fcd0cad René Nussbaumer
# the Free Software Foundation; either version 2 of the License, or
9 0fcd0cad René Nussbaumer
# (at your option) any later version.
10 0fcd0cad René Nussbaumer
#
11 0fcd0cad René Nussbaumer
# This program is distributed in the hope that it will be useful, but
12 0fcd0cad René Nussbaumer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 0fcd0cad René Nussbaumer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 0fcd0cad René Nussbaumer
# General Public License for more details.
15 0fcd0cad René Nussbaumer
#
16 0fcd0cad René Nussbaumer
# You should have received a copy of the GNU General Public License
17 0fcd0cad René Nussbaumer
# along with this program; if not, write to the Free Software
18 0fcd0cad René Nussbaumer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 0fcd0cad René Nussbaumer
# 02110-1301, USA.
20 0fcd0cad René Nussbaumer
21 0fcd0cad René Nussbaumer
22 0fcd0cad René Nussbaumer
"""Module implementing the iallocator code."""
23 0fcd0cad René Nussbaumer
24 0fcd0cad René Nussbaumer
from ganeti import compat
25 0fcd0cad René Nussbaumer
from ganeti import constants
26 0fcd0cad René Nussbaumer
from ganeti import errors
27 0fcd0cad René Nussbaumer
from ganeti import ht
28 473d87a3 Iustin Pop
from ganeti import outils
29 0fcd0cad René Nussbaumer
from ganeti import opcodes
30 0fcd0cad René Nussbaumer
from ganeti import rpc
31 0fcd0cad René Nussbaumer
from ganeti import serializer
32 0fcd0cad René Nussbaumer
from ganeti import utils
33 0fcd0cad René Nussbaumer
34 0fcd0cad René Nussbaumer
import ganeti.masterd.instance as gmi
35 0fcd0cad René Nussbaumer
36 0fcd0cad René Nussbaumer
37 0fcd0cad René Nussbaumer
_STRING_LIST = ht.TListOf(ht.TString)
38 0fcd0cad René Nussbaumer
_JOB_LIST = ht.TListOf(ht.TListOf(ht.TStrictDict(True, False, {
39 0fcd0cad René Nussbaumer
   # pylint: disable=E1101
40 0fcd0cad René Nussbaumer
   # Class '...' has no 'OP_ID' member
41 0fcd0cad René Nussbaumer
   "OP_ID": ht.TElemOf([opcodes.OpInstanceFailover.OP_ID,
42 0fcd0cad René Nussbaumer
                        opcodes.OpInstanceMigrate.OP_ID,
43 3c286190 Dimitris Aragiorgis
                        opcodes.OpInstanceReplaceDisks.OP_ID]),
44 0fcd0cad René Nussbaumer
   })))
45 0fcd0cad René Nussbaumer
46 0fcd0cad René Nussbaumer
_NEVAC_MOVED = \
47 0fcd0cad René Nussbaumer
  ht.TListOf(ht.TAnd(ht.TIsLength(3),
48 0fcd0cad René Nussbaumer
                     ht.TItems([ht.TNonEmptyString,
49 0fcd0cad René Nussbaumer
                                ht.TNonEmptyString,
50 0fcd0cad René Nussbaumer
                                ht.TListOf(ht.TNonEmptyString),
51 0fcd0cad René Nussbaumer
                                ])))
52 0fcd0cad René Nussbaumer
_NEVAC_FAILED = \
53 0fcd0cad René Nussbaumer
  ht.TListOf(ht.TAnd(ht.TIsLength(2),
54 0fcd0cad René Nussbaumer
                     ht.TItems([ht.TNonEmptyString,
55 0fcd0cad René Nussbaumer
                                ht.TMaybeString,
56 0fcd0cad René Nussbaumer
                                ])))
57 0fcd0cad René Nussbaumer
_NEVAC_RESULT = ht.TAnd(ht.TIsLength(3),
58 0fcd0cad René Nussbaumer
                        ht.TItems([_NEVAC_MOVED, _NEVAC_FAILED, _JOB_LIST]))
59 0fcd0cad René Nussbaumer
60 3d7d3a12 René Nussbaumer
_INST_NAME = ("name", ht.TNonEmptyString)
61 3d7d3a12 René Nussbaumer
62 0fcd0cad René Nussbaumer
63 473d87a3 Iustin Pop
class _AutoReqParam(outils.AutoSlots):
64 0fcd0cad René Nussbaumer
  """Meta class for request definitions.
65 0fcd0cad René Nussbaumer

66 0fcd0cad René Nussbaumer
  """
67 0fcd0cad René Nussbaumer
  @classmethod
68 0fcd0cad René Nussbaumer
  def _GetSlots(mcs, attrs):
69 0fcd0cad René Nussbaumer
    """Extract the slots out of REQ_PARAMS.
70 0fcd0cad René Nussbaumer

71 0fcd0cad René Nussbaumer
    """
72 0fcd0cad René Nussbaumer
    params = attrs.setdefault("REQ_PARAMS", [])
73 0fcd0cad René Nussbaumer
    return [slot for (slot, _) in params]
74 0fcd0cad René Nussbaumer
75 0fcd0cad René Nussbaumer
76 473d87a3 Iustin Pop
class IARequestBase(outils.ValidatedSlots):
77 0fcd0cad René Nussbaumer
  """A generic IAllocator request object.
78 0fcd0cad René Nussbaumer

79 0fcd0cad René Nussbaumer
  """
80 0fcd0cad René Nussbaumer
  __metaclass__ = _AutoReqParam
81 0fcd0cad René Nussbaumer
82 0fcd0cad René Nussbaumer
  MODE = NotImplemented
83 776b6291 René Nussbaumer
  REQ_PARAMS = []
84 0fcd0cad René Nussbaumer
  REQ_RESULT = NotImplemented
85 0fcd0cad René Nussbaumer
86 0fcd0cad René Nussbaumer
  def __init__(self, **kwargs):
87 0fcd0cad René Nussbaumer
    """Constructor for IARequestBase.
88 0fcd0cad René Nussbaumer

89 0fcd0cad René Nussbaumer
    The constructor takes only keyword arguments and will set
90 0fcd0cad René Nussbaumer
    attributes on this object based on the passed arguments. As such,
91 0fcd0cad René Nussbaumer
    it means that you should not pass arguments which are not in the
92 0fcd0cad René Nussbaumer
    REQ_PARAMS attribute for this class.
93 0fcd0cad René Nussbaumer

94 0fcd0cad René Nussbaumer
    """
95 473d87a3 Iustin Pop
    outils.ValidatedSlots.__init__(self, **kwargs)
96 0fcd0cad René Nussbaumer
97 0fcd0cad René Nussbaumer
    self.Validate()
98 0fcd0cad René Nussbaumer
99 0fcd0cad René Nussbaumer
  def Validate(self):
100 0fcd0cad René Nussbaumer
    """Validates all parameters of the request.
101 0fcd0cad René Nussbaumer

102 0fcd0cad René Nussbaumer
    """
103 0fcd0cad René Nussbaumer
    assert self.MODE in constants.VALID_IALLOCATOR_MODES
104 0fcd0cad René Nussbaumer
105 0fcd0cad René Nussbaumer
    for (param, validator) in self.REQ_PARAMS:
106 0fcd0cad René Nussbaumer
      if not hasattr(self, param):
107 0fcd0cad René Nussbaumer
        raise errors.OpPrereqError("Request is missing '%s' parameter" % param,
108 0fcd0cad René Nussbaumer
                                   errors.ECODE_INVAL)
109 0fcd0cad René Nussbaumer
110 0fcd0cad René Nussbaumer
      value = getattr(self, param)
111 0fcd0cad René Nussbaumer
      if not validator(value):
112 0fcd0cad René Nussbaumer
        raise errors.OpPrereqError(("Request parameter '%s' has invalid"
113 0fcd0cad René Nussbaumer
                                    " type %s/value %s") %
114 0fcd0cad René Nussbaumer
                                    (param, type(value), value),
115 0fcd0cad René Nussbaumer
                                    errors.ECODE_INVAL)
116 0fcd0cad René Nussbaumer
117 0fcd0cad René Nussbaumer
  def GetRequest(self, cfg):
118 0fcd0cad René Nussbaumer
    """Gets the request data dict.
119 0fcd0cad René Nussbaumer

120 0fcd0cad René Nussbaumer
    @param cfg: The configuration instance
121 0fcd0cad René Nussbaumer

122 0fcd0cad René Nussbaumer
    """
123 0fcd0cad René Nussbaumer
    raise NotImplementedError
124 0fcd0cad René Nussbaumer
125 0fcd0cad René Nussbaumer
  def ValidateResult(self, ia, result):
126 0fcd0cad René Nussbaumer
    """Validates the result of an request.
127 0fcd0cad René Nussbaumer

128 0fcd0cad René Nussbaumer
    @param ia: The IAllocator instance
129 0fcd0cad René Nussbaumer
    @param result: The IAllocator run result
130 776b6291 René Nussbaumer
    @raises ResultValidationError: If validation fails
131 0fcd0cad René Nussbaumer

132 0fcd0cad René Nussbaumer
    """
133 69b0d82a René Nussbaumer
    if ia.success and not self.REQ_RESULT(result):
134 776b6291 René Nussbaumer
      raise errors.ResultValidationError("iallocator returned invalid result,"
135 776b6291 René Nussbaumer
                                         " expected %s, got %s" %
136 776b6291 René Nussbaumer
                                         (self.REQ_RESULT, result))
137 0fcd0cad René Nussbaumer
138 0fcd0cad René Nussbaumer
139 0fcd0cad René Nussbaumer
class IAReqInstanceAlloc(IARequestBase):
140 0fcd0cad René Nussbaumer
  """An instance allocation request.
141 0fcd0cad René Nussbaumer

142 0fcd0cad René Nussbaumer
  """
143 c269efc3 René Nussbaumer
  # pylint: disable=E1101
144 0fcd0cad René Nussbaumer
  MODE = constants.IALLOCATOR_MODE_ALLOC
145 0fcd0cad René Nussbaumer
  REQ_PARAMS = [
146 3d7d3a12 René Nussbaumer
    _INST_NAME,
147 2c9fa1ff Iustin Pop
    ("memory", ht.TNonNegativeInt),
148 2c9fa1ff Iustin Pop
    ("spindle_use", ht.TNonNegativeInt),
149 0fcd0cad René Nussbaumer
    ("disks", ht.TListOf(ht.TDict)),
150 0fcd0cad René Nussbaumer
    ("disk_template", ht.TString),
151 0fcd0cad René Nussbaumer
    ("os", ht.TString),
152 0fcd0cad René Nussbaumer
    ("tags", _STRING_LIST),
153 0fcd0cad René Nussbaumer
    ("nics", ht.TListOf(ht.TDict)),
154 0fcd0cad René Nussbaumer
    ("vcpus", ht.TInt),
155 0fcd0cad René Nussbaumer
    ("hypervisor", ht.TString),
156 fb60bc6a Michael Hanselmann
    ("node_whitelist", ht.TMaybeListOf(ht.TNonEmptyString)),
157 0fcd0cad René Nussbaumer
    ]
158 0fcd0cad René Nussbaumer
  REQ_RESULT = ht.TList
159 0fcd0cad René Nussbaumer
160 776b6291 René Nussbaumer
  def RequiredNodes(self):
161 776b6291 René Nussbaumer
    """Calculates the required nodes based on the disk_template.
162 776b6291 René Nussbaumer

163 776b6291 René Nussbaumer
    """
164 776b6291 René Nussbaumer
    if self.disk_template in constants.DTS_INT_MIRROR:
165 776b6291 René Nussbaumer
      return 2
166 776b6291 René Nussbaumer
    else:
167 776b6291 René Nussbaumer
      return 1
168 776b6291 René Nussbaumer
169 0fcd0cad René Nussbaumer
  def GetRequest(self, cfg):
170 0fcd0cad René Nussbaumer
    """Requests a new instance.
171 0fcd0cad René Nussbaumer

172 0fcd0cad René Nussbaumer
    The checks for the completeness of the opcode must have already been
173 0fcd0cad René Nussbaumer
    done.
174 0fcd0cad René Nussbaumer

175 0fcd0cad René Nussbaumer
    """
176 0fcd0cad René Nussbaumer
    disk_space = gmi.ComputeDiskSize(self.disk_template, self.disks)
177 0fcd0cad René Nussbaumer
178 0fcd0cad René Nussbaumer
    return {
179 0fcd0cad René Nussbaumer
      "name": self.name,
180 0fcd0cad René Nussbaumer
      "disk_template": self.disk_template,
181 0fcd0cad René Nussbaumer
      "tags": self.tags,
182 0fcd0cad René Nussbaumer
      "os": self.os,
183 0fcd0cad René Nussbaumer
      "vcpus": self.vcpus,
184 0fcd0cad René Nussbaumer
      "memory": self.memory,
185 0fcd0cad René Nussbaumer
      "spindle_use": self.spindle_use,
186 0fcd0cad René Nussbaumer
      "disks": self.disks,
187 0fcd0cad René Nussbaumer
      "disk_space_total": disk_space,
188 0fcd0cad René Nussbaumer
      "nics": self.nics,
189 776b6291 René Nussbaumer
      "required_nodes": self.RequiredNodes(),
190 0fcd0cad René Nussbaumer
      "hypervisor": self.hypervisor,
191 0fcd0cad René Nussbaumer
      }
192 0fcd0cad René Nussbaumer
193 776b6291 René Nussbaumer
  def ValidateResult(self, ia, result):
194 776b6291 René Nussbaumer
    """Validates an single instance allocation request.
195 776b6291 René Nussbaumer

196 776b6291 René Nussbaumer
    """
197 776b6291 René Nussbaumer
    IARequestBase.ValidateResult(self, ia, result)
198 776b6291 René Nussbaumer
199 579f4ee5 René Nussbaumer
    if ia.success and len(result) != self.RequiredNodes():
200 776b6291 René Nussbaumer
      raise errors.ResultValidationError("iallocator returned invalid number"
201 776b6291 René Nussbaumer
                                         " of nodes (%s), required %s" %
202 776b6291 René Nussbaumer
                                         (len(result), self.RequiredNodes()))
203 776b6291 René Nussbaumer
204 0fcd0cad René Nussbaumer
205 b1e47e2d René Nussbaumer
class IAReqMultiInstanceAlloc(IARequestBase):
206 b1e47e2d René Nussbaumer
  """An multi instance allocation request.
207 b1e47e2d René Nussbaumer

208 b1e47e2d René Nussbaumer
  """
209 c269efc3 René Nussbaumer
  # pylint: disable=E1101
210 b1e47e2d René Nussbaumer
  MODE = constants.IALLOCATOR_MODE_MULTI_ALLOC
211 b1e47e2d René Nussbaumer
  REQ_PARAMS = [
212 3c286190 Dimitris Aragiorgis
    ("instances", ht.TListOf(ht.TInstanceOf(IAReqInstanceAlloc))),
213 b1e47e2d René Nussbaumer
    ]
214 b1e47e2d René Nussbaumer
  _MASUCCESS = \
215 b1e47e2d René Nussbaumer
    ht.TListOf(ht.TAnd(ht.TIsLength(2),
216 b1e47e2d René Nussbaumer
                       ht.TItems([ht.TNonEmptyString,
217 b1e47e2d René Nussbaumer
                                  ht.TListOf(ht.TNonEmptyString),
218 b1e47e2d René Nussbaumer
                                  ])))
219 b1e47e2d René Nussbaumer
  _MAFAILED = ht.TListOf(ht.TNonEmptyString)
220 32eae1ec René Nussbaumer
  REQ_RESULT = ht.TAnd(ht.TList, ht.TIsLength(2),
221 32eae1ec René Nussbaumer
                       ht.TItems([_MASUCCESS, _MAFAILED]))
222 b1e47e2d René Nussbaumer
223 b1e47e2d René Nussbaumer
  def GetRequest(self, cfg):
224 b1e47e2d René Nussbaumer
    return {
225 3c286190 Dimitris Aragiorgis
      "instances": [iareq.GetRequest(cfg) for iareq in self.instances],
226 b1e47e2d René Nussbaumer
      }
227 b1e47e2d René Nussbaumer
228 b1e47e2d René Nussbaumer
229 0fcd0cad René Nussbaumer
class IAReqRelocate(IARequestBase):
230 0fcd0cad René Nussbaumer
  """A relocation request.
231 0fcd0cad René Nussbaumer

232 0fcd0cad René Nussbaumer
  """
233 c269efc3 René Nussbaumer
  # pylint: disable=E1101
234 0fcd0cad René Nussbaumer
  MODE = constants.IALLOCATOR_MODE_RELOC
235 0fcd0cad René Nussbaumer
  REQ_PARAMS = [
236 3d7d3a12 René Nussbaumer
    _INST_NAME,
237 0fcd0cad René Nussbaumer
    ("relocate_from", _STRING_LIST),
238 0fcd0cad René Nussbaumer
    ]
239 0fcd0cad René Nussbaumer
  REQ_RESULT = ht.TList
240 0fcd0cad René Nussbaumer
241 0fcd0cad René Nussbaumer
  def GetRequest(self, cfg):
242 0fcd0cad René Nussbaumer
    """Request an relocation of an instance
243 0fcd0cad René Nussbaumer

244 0fcd0cad René Nussbaumer
    The checks for the completeness of the opcode must have already been
245 0fcd0cad René Nussbaumer
    done.
246 0fcd0cad René Nussbaumer

247 0fcd0cad René Nussbaumer
    """
248 0fcd0cad René Nussbaumer
    instance = cfg.GetInstanceInfo(self.name)
249 0fcd0cad René Nussbaumer
    if instance is None:
250 0fcd0cad René Nussbaumer
      raise errors.ProgrammerError("Unknown instance '%s' passed to"
251 0fcd0cad René Nussbaumer
                                   " IAllocator" % self.name)
252 0fcd0cad René Nussbaumer
253 0fcd0cad René Nussbaumer
    if instance.disk_template not in constants.DTS_MIRRORED:
254 0fcd0cad René Nussbaumer
      raise errors.OpPrereqError("Can't relocate non-mirrored instances",
255 0fcd0cad René Nussbaumer
                                 errors.ECODE_INVAL)
256 0fcd0cad René Nussbaumer
257 6953756f René Nussbaumer
    if (instance.disk_template in constants.DTS_INT_MIRROR and
258 6953756f René Nussbaumer
        len(instance.secondary_nodes) != 1):
259 0fcd0cad René Nussbaumer
      raise errors.OpPrereqError("Instance has not exactly one secondary node",
260 0fcd0cad René Nussbaumer
                                 errors.ECODE_STATE)
261 0fcd0cad René Nussbaumer
262 0fcd0cad René Nussbaumer
    disk_sizes = [{constants.IDISK_SIZE: disk.size} for disk in instance.disks]
263 0fcd0cad René Nussbaumer
    disk_space = gmi.ComputeDiskSize(instance.disk_template, disk_sizes)
264 0fcd0cad René Nussbaumer
265 0fcd0cad René Nussbaumer
    return {
266 0fcd0cad René Nussbaumer
      "name": self.name,
267 0fcd0cad René Nussbaumer
      "disk_space_total": disk_space,
268 776b6291 René Nussbaumer
      "required_nodes": 1,
269 0fcd0cad René Nussbaumer
      "relocate_from": self.relocate_from,
270 0fcd0cad René Nussbaumer
      }
271 0fcd0cad René Nussbaumer
272 0fcd0cad René Nussbaumer
  def ValidateResult(self, ia, result):
273 0fcd0cad René Nussbaumer
    """Validates the result of an relocation request.
274 0fcd0cad René Nussbaumer

275 0fcd0cad René Nussbaumer
    """
276 776b6291 René Nussbaumer
    IARequestBase.ValidateResult(self, ia, result)
277 776b6291 René Nussbaumer
278 0fcd0cad René Nussbaumer
    node2group = dict((name, ndata["group"])
279 0fcd0cad René Nussbaumer
                      for (name, ndata) in ia.in_data["nodes"].items())
280 0fcd0cad René Nussbaumer
281 0fcd0cad René Nussbaumer
    fn = compat.partial(self._NodesToGroups, node2group,
282 0fcd0cad René Nussbaumer
                        ia.in_data["nodegroups"])
283 0fcd0cad René Nussbaumer
284 0fcd0cad René Nussbaumer
    instance = ia.cfg.GetInstanceInfo(self.name)
285 0fcd0cad René Nussbaumer
    request_groups = fn(self.relocate_from + [instance.primary_node])
286 0fcd0cad René Nussbaumer
    result_groups = fn(result + [instance.primary_node])
287 0fcd0cad René Nussbaumer
288 0fcd0cad René Nussbaumer
    if ia.success and not set(result_groups).issubset(request_groups):
289 776b6291 René Nussbaumer
      raise errors.ResultValidationError("Groups of nodes returned by"
290 776b6291 René Nussbaumer
                                         "iallocator (%s) differ from original"
291 776b6291 René Nussbaumer
                                         " groups (%s)" %
292 776b6291 René Nussbaumer
                                         (utils.CommaJoin(result_groups),
293 776b6291 René Nussbaumer
                                          utils.CommaJoin(request_groups)))
294 0fcd0cad René Nussbaumer
295 0fcd0cad René Nussbaumer
  @staticmethod
296 0fcd0cad René Nussbaumer
  def _NodesToGroups(node2group, groups, nodes):
297 0fcd0cad René Nussbaumer
    """Returns a list of unique group names for a list of nodes.
298 0fcd0cad René Nussbaumer

299 0fcd0cad René Nussbaumer
    @type node2group: dict
300 0fcd0cad René Nussbaumer
    @param node2group: Map from node name to group UUID
301 0fcd0cad René Nussbaumer
    @type groups: dict
302 0fcd0cad René Nussbaumer
    @param groups: Group information
303 0fcd0cad René Nussbaumer
    @type nodes: list
304 0fcd0cad René Nussbaumer
    @param nodes: Node names
305 0fcd0cad René Nussbaumer

306 0fcd0cad René Nussbaumer
    """
307 0fcd0cad René Nussbaumer
    result = set()
308 0fcd0cad René Nussbaumer
309 0fcd0cad René Nussbaumer
    for node in nodes:
310 0fcd0cad René Nussbaumer
      try:
311 0fcd0cad René Nussbaumer
        group_uuid = node2group[node]
312 0fcd0cad René Nussbaumer
      except KeyError:
313 0fcd0cad René Nussbaumer
        # Ignore unknown node
314 0fcd0cad René Nussbaumer
        pass
315 0fcd0cad René Nussbaumer
      else:
316 0fcd0cad René Nussbaumer
        try:
317 0fcd0cad René Nussbaumer
          group = groups[group_uuid]
318 0fcd0cad René Nussbaumer
        except KeyError:
319 0fcd0cad René Nussbaumer
          # Can't find group, let's use UUID
320 0fcd0cad René Nussbaumer
          group_name = group_uuid
321 0fcd0cad René Nussbaumer
        else:
322 0fcd0cad René Nussbaumer
          group_name = group["name"]
323 0fcd0cad René Nussbaumer
324 0fcd0cad René Nussbaumer
        result.add(group_name)
325 0fcd0cad René Nussbaumer
326 0fcd0cad René Nussbaumer
    return sorted(result)
327 0fcd0cad René Nussbaumer
328 0fcd0cad René Nussbaumer
329 0fcd0cad René Nussbaumer
class IAReqNodeEvac(IARequestBase):
330 0fcd0cad René Nussbaumer
  """A node evacuation request.
331 0fcd0cad René Nussbaumer

332 0fcd0cad René Nussbaumer
  """
333 c269efc3 René Nussbaumer
  # pylint: disable=E1101
334 0fcd0cad René Nussbaumer
  MODE = constants.IALLOCATOR_MODE_NODE_EVAC
335 0fcd0cad René Nussbaumer
  REQ_PARAMS = [
336 0fcd0cad René Nussbaumer
    ("instances", _STRING_LIST),
337 0fcd0cad René Nussbaumer
    ("evac_mode", ht.TElemOf(constants.IALLOCATOR_NEVAC_MODES)),
338 0fcd0cad René Nussbaumer
    ]
339 0fcd0cad René Nussbaumer
  REQ_RESULT = _NEVAC_RESULT
340 0fcd0cad René Nussbaumer
341 0fcd0cad René Nussbaumer
  def GetRequest(self, cfg):
342 0fcd0cad René Nussbaumer
    """Get data for node-evacuate requests.
343 0fcd0cad René Nussbaumer

344 0fcd0cad René Nussbaumer
    """
345 0fcd0cad René Nussbaumer
    return {
346 0fcd0cad René Nussbaumer
      "instances": self.instances,
347 0fcd0cad René Nussbaumer
      "evac_mode": self.evac_mode,
348 0fcd0cad René Nussbaumer
      }
349 0fcd0cad René Nussbaumer
350 0fcd0cad René Nussbaumer
351 0fcd0cad René Nussbaumer
class IAReqGroupChange(IARequestBase):
352 0fcd0cad René Nussbaumer
  """A group change request.
353 0fcd0cad René Nussbaumer

354 0fcd0cad René Nussbaumer
  """
355 c269efc3 René Nussbaumer
  # pylint: disable=E1101
356 0fcd0cad René Nussbaumer
  MODE = constants.IALLOCATOR_MODE_CHG_GROUP
357 0fcd0cad René Nussbaumer
  REQ_PARAMS = [
358 0fcd0cad René Nussbaumer
    ("instances", _STRING_LIST),
359 0fcd0cad René Nussbaumer
    ("target_groups", _STRING_LIST),
360 0fcd0cad René Nussbaumer
    ]
361 0fcd0cad René Nussbaumer
  REQ_RESULT = _NEVAC_RESULT
362 0fcd0cad René Nussbaumer
363 0fcd0cad René Nussbaumer
  def GetRequest(self, cfg):
364 0fcd0cad René Nussbaumer
    """Get data for node-evacuate requests.
365 0fcd0cad René Nussbaumer

366 0fcd0cad René Nussbaumer
    """
367 0fcd0cad René Nussbaumer
    return {
368 0fcd0cad René Nussbaumer
      "instances": self.instances,
369 0fcd0cad René Nussbaumer
      "target_groups": self.target_groups,
370 0fcd0cad René Nussbaumer
      }
371 0fcd0cad René Nussbaumer
372 0fcd0cad René Nussbaumer
373 0fcd0cad René Nussbaumer
class IAllocator(object):
374 0fcd0cad René Nussbaumer
  """IAllocator framework.
375 0fcd0cad René Nussbaumer

376 0fcd0cad René Nussbaumer
  An IAllocator instance has three sets of attributes:
377 0fcd0cad René Nussbaumer
    - cfg that is needed to query the cluster
378 0fcd0cad René Nussbaumer
    - input data (all members of the _KEYS class attribute are required)
379 0fcd0cad René Nussbaumer
    - four buffer attributes (in|out_data|text), that represent the
380 0fcd0cad René Nussbaumer
      input (to the external script) in text and data structure format,
381 0fcd0cad René Nussbaumer
      and the output from it, again in two formats
382 0fcd0cad René Nussbaumer
    - the result variables from the script (success, info, nodes) for
383 0fcd0cad René Nussbaumer
      easy usage
384 0fcd0cad René Nussbaumer

385 0fcd0cad René Nussbaumer
  """
386 0fcd0cad René Nussbaumer
  # pylint: disable=R0902
387 0fcd0cad René Nussbaumer
  # lots of instance attributes
388 0fcd0cad René Nussbaumer
389 0fcd0cad René Nussbaumer
  def __init__(self, cfg, rpc_runner, req):
390 0fcd0cad René Nussbaumer
    self.cfg = cfg
391 0fcd0cad René Nussbaumer
    self.rpc = rpc_runner
392 0fcd0cad René Nussbaumer
    self.req = req
393 0fcd0cad René Nussbaumer
    # init buffer variables
394 0fcd0cad René Nussbaumer
    self.in_text = self.out_text = self.in_data = self.out_data = None
395 0fcd0cad René Nussbaumer
    # init result fields
396 0fcd0cad René Nussbaumer
    self.success = self.info = self.result = None
397 0fcd0cad René Nussbaumer
398 0fcd0cad René Nussbaumer
    self._BuildInputData(req)
399 0fcd0cad René Nussbaumer
400 0fcd0cad René Nussbaumer
  def _ComputeClusterData(self):
401 0fcd0cad René Nussbaumer
    """Compute the generic allocator input data.
402 0fcd0cad René Nussbaumer

403 0fcd0cad René Nussbaumer
    This is the data that is independent of the actual operation.
404 0fcd0cad René Nussbaumer

405 0fcd0cad René Nussbaumer
    """
406 0fcd0cad René Nussbaumer
    cfg = self.cfg
407 0fcd0cad René Nussbaumer
    cluster_info = cfg.GetClusterInfo()
408 0fcd0cad René Nussbaumer
    # cluster data
409 0fcd0cad René Nussbaumer
    data = {
410 0fcd0cad René Nussbaumer
      "version": constants.IALLOCATOR_VERSION,
411 0fcd0cad René Nussbaumer
      "cluster_name": cfg.GetClusterName(),
412 0fcd0cad René Nussbaumer
      "cluster_tags": list(cluster_info.GetTags()),
413 0fcd0cad René Nussbaumer
      "enabled_hypervisors": list(cluster_info.enabled_hypervisors),
414 0fcd0cad René Nussbaumer
      "ipolicy": cluster_info.ipolicy,
415 0fcd0cad René Nussbaumer
      }
416 0fcd0cad René Nussbaumer
    ninfo = cfg.GetAllNodesInfo()
417 0fcd0cad René Nussbaumer
    iinfo = cfg.GetAllInstancesInfo().values()
418 0fcd0cad René Nussbaumer
    i_list = [(inst, cluster_info.FillBE(inst)) for inst in iinfo]
419 0fcd0cad René Nussbaumer
420 0fcd0cad René Nussbaumer
    # node data
421 0fcd0cad René Nussbaumer
    node_list = [n.name for n in ninfo.values() if n.vm_capable]
422 0fcd0cad René Nussbaumer
423 0fcd0cad René Nussbaumer
    if isinstance(self.req, IAReqInstanceAlloc):
424 0fcd0cad René Nussbaumer
      hypervisor_name = self.req.hypervisor
425 fb60bc6a Michael Hanselmann
      node_whitelist = self.req.node_whitelist
426 0fcd0cad René Nussbaumer
    elif isinstance(self.req, IAReqRelocate):
427 0fcd0cad René Nussbaumer
      hypervisor_name = cfg.GetInstanceInfo(self.req.name).hypervisor
428 fb60bc6a Michael Hanselmann
      node_whitelist = None
429 0fcd0cad René Nussbaumer
    else:
430 0fcd0cad René Nussbaumer
      hypervisor_name = cluster_info.primary_hypervisor
431 fb60bc6a Michael Hanselmann
      node_whitelist = None
432 0fcd0cad René Nussbaumer
433 319322a7 Bernardo Dal Seno
    es_flags = rpc.GetExclusiveStorageForNodeNames(cfg, node_list)
434 36a566e8 Iustin Pop
    vg_name = cfg.GetVGName()
435 36a566e8 Iustin Pop
    if vg_name is not None:
436 36a566e8 Iustin Pop
      has_lvm = True
437 36a566e8 Iustin Pop
      vg_req = [vg_name]
438 36a566e8 Iustin Pop
    else:
439 36a566e8 Iustin Pop
      has_lvm = False
440 36a566e8 Iustin Pop
      vg_req = []
441 36a566e8 Iustin Pop
    node_data = self.rpc.call_node_info(node_list, vg_req,
442 319322a7 Bernardo Dal Seno
                                        [hypervisor_name], es_flags)
443 0fcd0cad René Nussbaumer
    node_iinfo = \
444 0fcd0cad René Nussbaumer
      self.rpc.call_all_instances_info(node_list,
445 0fcd0cad René Nussbaumer
                                       cluster_info.enabled_hypervisors)
446 0fcd0cad René Nussbaumer
447 0fcd0cad René Nussbaumer
    data["nodegroups"] = self._ComputeNodeGroupData(cfg)
448 0fcd0cad René Nussbaumer
449 fb60bc6a Michael Hanselmann
    config_ndata = self._ComputeBasicNodeData(cfg, ninfo, node_whitelist)
450 0fcd0cad René Nussbaumer
    data["nodes"] = self._ComputeDynamicNodeData(ninfo, node_data, node_iinfo,
451 36a566e8 Iustin Pop
                                                 i_list, config_ndata, has_lvm)
452 0fcd0cad René Nussbaumer
    assert len(data["nodes"]) == len(ninfo), \
453 0fcd0cad René Nussbaumer
        "Incomplete node data computed"
454 0fcd0cad René Nussbaumer
455 0fcd0cad René Nussbaumer
    data["instances"] = self._ComputeInstanceData(cluster_info, i_list)
456 0fcd0cad René Nussbaumer
457 0fcd0cad René Nussbaumer
    self.in_data = data
458 0fcd0cad René Nussbaumer
459 0fcd0cad René Nussbaumer
  @staticmethod
460 0fcd0cad René Nussbaumer
  def _ComputeNodeGroupData(cfg):
461 0fcd0cad René Nussbaumer
    """Compute node groups data.
462 0fcd0cad René Nussbaumer

463 0fcd0cad René Nussbaumer
    """
464 0fcd0cad René Nussbaumer
    cluster = cfg.GetClusterInfo()
465 0fcd0cad René Nussbaumer
    ng = dict((guuid, {
466 0fcd0cad René Nussbaumer
      "name": gdata.name,
467 0fcd0cad René Nussbaumer
      "alloc_policy": gdata.alloc_policy,
468 0fcd0cad René Nussbaumer
      "ipolicy": gmi.CalculateGroupIPolicy(cluster, gdata),
469 f1222089 Dimitris Aragiorgis
      "tags": list(gdata.GetTags()),
470 0fcd0cad René Nussbaumer
      })
471 0fcd0cad René Nussbaumer
      for guuid, gdata in cfg.GetAllNodeGroupsInfo().items())
472 0fcd0cad René Nussbaumer
473 0fcd0cad René Nussbaumer
    return ng
474 0fcd0cad René Nussbaumer
475 0fcd0cad René Nussbaumer
  @staticmethod
476 fb60bc6a Michael Hanselmann
  def _ComputeBasicNodeData(cfg, node_cfg, node_whitelist):
477 0fcd0cad René Nussbaumer
    """Compute global node data.
478 0fcd0cad René Nussbaumer

479 0fcd0cad René Nussbaumer
    @rtype: dict
480 0fcd0cad René Nussbaumer
    @returns: a dict of name: (node dict, node config)
481 0fcd0cad René Nussbaumer

482 0fcd0cad René Nussbaumer
    """
483 0fcd0cad René Nussbaumer
    # fill in static (config-based) values
484 0fcd0cad René Nussbaumer
    node_results = dict((ninfo.name, {
485 0fcd0cad René Nussbaumer
      "tags": list(ninfo.GetTags()),
486 0fcd0cad René Nussbaumer
      "primary_ip": ninfo.primary_ip,
487 0fcd0cad René Nussbaumer
      "secondary_ip": ninfo.secondary_ip,
488 fb60bc6a Michael Hanselmann
      "offline": (ninfo.offline or
489 fb60bc6a Michael Hanselmann
                  not (node_whitelist is None or
490 fb60bc6a Michael Hanselmann
                       ninfo.name in node_whitelist)),
491 0fcd0cad René Nussbaumer
      "drained": ninfo.drained,
492 0fcd0cad René Nussbaumer
      "master_candidate": ninfo.master_candidate,
493 0fcd0cad René Nussbaumer
      "group": ninfo.group,
494 0fcd0cad René Nussbaumer
      "master_capable": ninfo.master_capable,
495 0fcd0cad René Nussbaumer
      "vm_capable": ninfo.vm_capable,
496 0fcd0cad René Nussbaumer
      "ndparams": cfg.GetNdParams(ninfo),
497 0fcd0cad René Nussbaumer
      })
498 0fcd0cad René Nussbaumer
      for ninfo in node_cfg.values())
499 0fcd0cad René Nussbaumer
500 0fcd0cad René Nussbaumer
    return node_results
501 0fcd0cad René Nussbaumer
502 0fcd0cad René Nussbaumer
  @staticmethod
503 0fcd0cad René Nussbaumer
  def _ComputeDynamicNodeData(node_cfg, node_data, node_iinfo, i_list,
504 36a566e8 Iustin Pop
                              node_results, has_lvm):
505 0fcd0cad René Nussbaumer
    """Compute global node data.
506 0fcd0cad René Nussbaumer

507 0fcd0cad René Nussbaumer
    @param node_results: the basic node structures as filled from the config
508 0fcd0cad René Nussbaumer

509 0fcd0cad René Nussbaumer
    """
510 0fcd0cad René Nussbaumer
    #TODO(dynmem): compute the right data on MAX and MIN memory
511 0fcd0cad René Nussbaumer
    # make a copy of the current dict
512 0fcd0cad René Nussbaumer
    node_results = dict(node_results)
513 0fcd0cad René Nussbaumer
    for nname, nresult in node_data.items():
514 0fcd0cad René Nussbaumer
      assert nname in node_results, "Missing basic data for node %s" % nname
515 0fcd0cad René Nussbaumer
      ninfo = node_cfg[nname]
516 0fcd0cad René Nussbaumer
517 0fcd0cad René Nussbaumer
      if not (ninfo.offline or ninfo.drained):
518 0fcd0cad René Nussbaumer
        nresult.Raise("Can't get data for node %s" % nname)
519 0fcd0cad René Nussbaumer
        node_iinfo[nname].Raise("Can't get node instance info from node %s" %
520 0fcd0cad René Nussbaumer
                                nname)
521 36a566e8 Iustin Pop
        remote_info = rpc.MakeLegacyNodeInfo(nresult.payload,
522 36a566e8 Iustin Pop
                                             require_vg_info=has_lvm)
523 0fcd0cad René Nussbaumer
524 36a566e8 Iustin Pop
        def get_attr(attr):
525 0fcd0cad René Nussbaumer
          if attr not in remote_info:
526 0fcd0cad René Nussbaumer
            raise errors.OpExecError("Node '%s' didn't return attribute"
527 0fcd0cad René Nussbaumer
                                     " '%s'" % (nname, attr))
528 36a566e8 Iustin Pop
          value = remote_info[attr]
529 36a566e8 Iustin Pop
          if not isinstance(value, int):
530 0fcd0cad René Nussbaumer
            raise errors.OpExecError("Node '%s' returned invalid value"
531 0fcd0cad René Nussbaumer
                                     " for '%s': %s" %
532 36a566e8 Iustin Pop
                                     (nname, attr, value))
533 36a566e8 Iustin Pop
          return value
534 36a566e8 Iustin Pop
535 36a566e8 Iustin Pop
        mem_free = get_attr("memory_free")
536 36a566e8 Iustin Pop
537 0fcd0cad René Nussbaumer
        # compute memory used by primary instances
538 0fcd0cad René Nussbaumer
        i_p_mem = i_p_up_mem = 0
539 0fcd0cad René Nussbaumer
        for iinfo, beinfo in i_list:
540 0fcd0cad René Nussbaumer
          if iinfo.primary_node == nname:
541 0fcd0cad René Nussbaumer
            i_p_mem += beinfo[constants.BE_MAXMEM]
542 0fcd0cad René Nussbaumer
            if iinfo.name not in node_iinfo[nname].payload:
543 0fcd0cad René Nussbaumer
              i_used_mem = 0
544 0fcd0cad René Nussbaumer
            else:
545 0fcd0cad René Nussbaumer
              i_used_mem = int(node_iinfo[nname].payload[iinfo.name]["memory"])
546 0fcd0cad René Nussbaumer
            i_mem_diff = beinfo[constants.BE_MAXMEM] - i_used_mem
547 36a566e8 Iustin Pop
            mem_free -= max(0, i_mem_diff)
548 0fcd0cad René Nussbaumer
549 0fcd0cad René Nussbaumer
            if iinfo.admin_state == constants.ADMINST_UP:
550 0fcd0cad René Nussbaumer
              i_p_up_mem += beinfo[constants.BE_MAXMEM]
551 0fcd0cad René Nussbaumer
552 36a566e8 Iustin Pop
        # TODO: replace this with proper storage reporting
553 36a566e8 Iustin Pop
        if has_lvm:
554 36a566e8 Iustin Pop
          total_disk = get_attr("vg_size")
555 36a566e8 Iustin Pop
          free_disk = get_attr("vg_free")
556 36a566e8 Iustin Pop
        else:
557 36a566e8 Iustin Pop
          # we didn't even ask the node for VG status, so use zeros
558 36a566e8 Iustin Pop
          total_disk = free_disk = 0
559 36a566e8 Iustin Pop
560 0fcd0cad René Nussbaumer
        # compute memory used by instances
561 0fcd0cad René Nussbaumer
        pnr_dyn = {
562 36a566e8 Iustin Pop
          "total_memory": get_attr("memory_total"),
563 36a566e8 Iustin Pop
          "reserved_memory": get_attr("memory_dom0"),
564 36a566e8 Iustin Pop
          "free_memory": mem_free,
565 36a566e8 Iustin Pop
          "total_disk": total_disk,
566 36a566e8 Iustin Pop
          "free_disk": free_disk,
567 36a566e8 Iustin Pop
          "total_cpus": get_attr("cpu_total"),
568 0fcd0cad René Nussbaumer
          "i_pri_memory": i_p_mem,
569 0fcd0cad René Nussbaumer
          "i_pri_up_memory": i_p_up_mem,
570 0fcd0cad René Nussbaumer
          }
571 0fcd0cad René Nussbaumer
        pnr_dyn.update(node_results[nname])
572 0fcd0cad René Nussbaumer
        node_results[nname] = pnr_dyn
573 0fcd0cad René Nussbaumer
574 0fcd0cad René Nussbaumer
    return node_results
575 0fcd0cad René Nussbaumer
576 0fcd0cad René Nussbaumer
  @staticmethod
577 0fcd0cad René Nussbaumer
  def _ComputeInstanceData(cluster_info, i_list):
578 0fcd0cad René Nussbaumer
    """Compute global instance data.
579 0fcd0cad René Nussbaumer

580 0fcd0cad René Nussbaumer
    """
581 0fcd0cad René Nussbaumer
    instance_data = {}
582 0fcd0cad René Nussbaumer
    for iinfo, beinfo in i_list:
583 0fcd0cad René Nussbaumer
      nic_data = []
584 0fcd0cad René Nussbaumer
      for nic in iinfo.nics:
585 0fcd0cad René Nussbaumer
        filled_params = cluster_info.SimpleFillNIC(nic.nicparams)
586 0fcd0cad René Nussbaumer
        nic_dict = {
587 0fcd0cad René Nussbaumer
          "mac": nic.mac,
588 0fcd0cad René Nussbaumer
          "ip": nic.ip,
589 0fcd0cad René Nussbaumer
          "mode": filled_params[constants.NIC_MODE],
590 0fcd0cad René Nussbaumer
          "link": filled_params[constants.NIC_LINK],
591 0fcd0cad René Nussbaumer
          }
592 0fcd0cad René Nussbaumer
        if filled_params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
593 0fcd0cad René Nussbaumer
          nic_dict["bridge"] = filled_params[constants.NIC_LINK]
594 0fcd0cad René Nussbaumer
        nic_data.append(nic_dict)
595 0fcd0cad René Nussbaumer
      pir = {
596 0fcd0cad René Nussbaumer
        "tags": list(iinfo.GetTags()),
597 0fcd0cad René Nussbaumer
        "admin_state": iinfo.admin_state,
598 0fcd0cad René Nussbaumer
        "vcpus": beinfo[constants.BE_VCPUS],
599 0fcd0cad René Nussbaumer
        "memory": beinfo[constants.BE_MAXMEM],
600 0fcd0cad René Nussbaumer
        "spindle_use": beinfo[constants.BE_SPINDLE_USE],
601 0fcd0cad René Nussbaumer
        "os": iinfo.os,
602 0fcd0cad René Nussbaumer
        "nodes": [iinfo.primary_node] + list(iinfo.secondary_nodes),
603 0fcd0cad René Nussbaumer
        "nics": nic_data,
604 0fcd0cad René Nussbaumer
        "disks": [{constants.IDISK_SIZE: dsk.size,
605 0fcd0cad René Nussbaumer
                   constants.IDISK_MODE: dsk.mode}
606 0fcd0cad René Nussbaumer
                  for dsk in iinfo.disks],
607 0fcd0cad René Nussbaumer
        "disk_template": iinfo.disk_template,
608 1d4a4b26 Thomas Thrainer
        "disks_active": iinfo.disks_active,
609 0fcd0cad René Nussbaumer
        "hypervisor": iinfo.hypervisor,
610 0fcd0cad René Nussbaumer
        }
611 0fcd0cad René Nussbaumer
      pir["disk_space_total"] = gmi.ComputeDiskSize(iinfo.disk_template,
612 0fcd0cad René Nussbaumer
                                                    pir["disks"])
613 0fcd0cad René Nussbaumer
      instance_data[iinfo.name] = pir
614 0fcd0cad René Nussbaumer
615 0fcd0cad René Nussbaumer
    return instance_data
616 0fcd0cad René Nussbaumer
617 0fcd0cad René Nussbaumer
  def _BuildInputData(self, req):
618 0fcd0cad René Nussbaumer
    """Build input data structures.
619 0fcd0cad René Nussbaumer

620 0fcd0cad René Nussbaumer
    """
621 0fcd0cad René Nussbaumer
    self._ComputeClusterData()
622 0fcd0cad René Nussbaumer
623 0fcd0cad René Nussbaumer
    request = req.GetRequest(self.cfg)
624 0fcd0cad René Nussbaumer
    request["type"] = req.MODE
625 0fcd0cad René Nussbaumer
    self.in_data["request"] = request
626 0fcd0cad René Nussbaumer
627 0fcd0cad René Nussbaumer
    self.in_text = serializer.Dump(self.in_data)
628 0fcd0cad René Nussbaumer
629 0fcd0cad René Nussbaumer
  def Run(self, name, validate=True, call_fn=None):
630 0fcd0cad René Nussbaumer
    """Run an instance allocator and return the results.
631 0fcd0cad René Nussbaumer

632 0fcd0cad René Nussbaumer
    """
633 0fcd0cad René Nussbaumer
    if call_fn is None:
634 0fcd0cad René Nussbaumer
      call_fn = self.rpc.call_iallocator_runner
635 0fcd0cad René Nussbaumer
636 0fcd0cad René Nussbaumer
    result = call_fn(self.cfg.GetMasterNode(), name, self.in_text)
637 0fcd0cad René Nussbaumer
    result.Raise("Failure while running the iallocator script")
638 0fcd0cad René Nussbaumer
639 0fcd0cad René Nussbaumer
    self.out_text = result.payload
640 0fcd0cad René Nussbaumer
    if validate:
641 0fcd0cad René Nussbaumer
      self._ValidateResult()
642 0fcd0cad René Nussbaumer
643 0fcd0cad René Nussbaumer
  def _ValidateResult(self):
644 0fcd0cad René Nussbaumer
    """Process the allocator results.
645 0fcd0cad René Nussbaumer

646 0fcd0cad René Nussbaumer
    This will process and if successful save the result in
647 0fcd0cad René Nussbaumer
    self.out_data and the other parameters.
648 0fcd0cad René Nussbaumer

649 0fcd0cad René Nussbaumer
    """
650 0fcd0cad René Nussbaumer
    try:
651 0fcd0cad René Nussbaumer
      rdict = serializer.Load(self.out_text)
652 0fcd0cad René Nussbaumer
    except Exception, err:
653 0fcd0cad René Nussbaumer
      raise errors.OpExecError("Can't parse iallocator results: %s" % str(err))
654 0fcd0cad René Nussbaumer
655 0fcd0cad René Nussbaumer
    if not isinstance(rdict, dict):
656 0fcd0cad René Nussbaumer
      raise errors.OpExecError("Can't parse iallocator results: not a dict")
657 0fcd0cad René Nussbaumer
658 0fcd0cad René Nussbaumer
    # TODO: remove backwards compatiblity in later versions
659 0fcd0cad René Nussbaumer
    if "nodes" in rdict and "result" not in rdict:
660 0fcd0cad René Nussbaumer
      rdict["result"] = rdict["nodes"]
661 0fcd0cad René Nussbaumer
      del rdict["nodes"]
662 0fcd0cad René Nussbaumer
663 0fcd0cad René Nussbaumer
    for key in "success", "info", "result":
664 0fcd0cad René Nussbaumer
      if key not in rdict:
665 0fcd0cad René Nussbaumer
        raise errors.OpExecError("Can't parse iallocator results:"
666 0fcd0cad René Nussbaumer
                                 " missing key '%s'" % key)
667 0fcd0cad René Nussbaumer
      setattr(self, key, rdict[key])
668 0fcd0cad René Nussbaumer
669 0fcd0cad René Nussbaumer
    self.req.ValidateResult(self, self.result)
670 0fcd0cad René Nussbaumer
    self.out_data = rdict