Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 13f1af63

History | View | Annotate | Download (26.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Transportable objects for Ganeti.
23 a8083063 Iustin Pop

24 a8083063 Iustin Pop
This module provides small, mostly data-only objects which are safe to
25 a8083063 Iustin Pop
pass to and from external parties.
26 a8083063 Iustin Pop

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import ConfigParser
31 5c947f38 Iustin Pop
import re
32 5bf7b5cf Iustin Pop
import copy
33 d5835922 Michael Hanselmann
from cStringIO import StringIO
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
from ganeti import errors
36 5c947f38 Iustin Pop
from ganeti import constants
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
40 abe609b2 Guido Trotter
           "OS", "Node", "Cluster", "FillDict"]
41 a8083063 Iustin Pop
42 abe609b2 Guido Trotter
def FillDict(defaults_dict, custom_dict):
43 abe609b2 Guido Trotter
    """Basic function to apply settings on top a default dict.
44 abe609b2 Guido Trotter

45 abe609b2 Guido Trotter
    @type defaults_dict: dict
46 abe609b2 Guido Trotter
    @param defaults_dict: dictionary holding the default values
47 abe609b2 Guido Trotter
    @type custom_dict: dict
48 abe609b2 Guido Trotter
    @param custom_dict: dictionary holding customized value
49 abe609b2 Guido Trotter
    @rtype: dict
50 abe609b2 Guido Trotter
    @return: dict with the 'full' values
51 abe609b2 Guido Trotter

52 abe609b2 Guido Trotter
    """
53 abe609b2 Guido Trotter
    ret_dict = copy.deepcopy(defaults_dict)
54 abe609b2 Guido Trotter
    ret_dict.update(custom_dict)
55 abe609b2 Guido Trotter
    return ret_dict
56 a8083063 Iustin Pop
57 6e34b628 Guido Trotter
58 6e34b628 Guido Trotter
def UpgradeGroupedParams(target, defaults):
59 6e34b628 Guido Trotter
  """Update all groups for the target parameter.
60 6e34b628 Guido Trotter

61 6e34b628 Guido Trotter
  @type target: dict of dicts
62 6e34b628 Guido Trotter
  @param target: {group: {parameter: value}}
63 6e34b628 Guido Trotter
  @type defaults: dict
64 6e34b628 Guido Trotter
  @param defaults: default parameter values
65 6e34b628 Guido Trotter

66 6e34b628 Guido Trotter
  """
67 6e34b628 Guido Trotter
  if target is None:
68 6e34b628 Guido Trotter
    target = {constants.PP_DEFAULT: defaults}
69 6e34b628 Guido Trotter
  else:
70 6e34b628 Guido Trotter
    for group in target:
71 6e34b628 Guido Trotter
      target[group] = FillDict(defaults, target[group])
72 6e34b628 Guido Trotter
  return target
73 6e34b628 Guido Trotter
74 6e34b628 Guido Trotter
75 a8083063 Iustin Pop
class ConfigObject(object):
76 a8083063 Iustin Pop
  """A generic config object.
77 a8083063 Iustin Pop

78 a8083063 Iustin Pop
  It has the following properties:
79 a8083063 Iustin Pop

80 a8083063 Iustin Pop
    - provides somewhat safe recursive unpickling and pickling for its classes
81 a8083063 Iustin Pop
    - unset attributes which are defined in slots are always returned
82 a8083063 Iustin Pop
      as None instead of raising an error
83 a8083063 Iustin Pop

84 a8083063 Iustin Pop
  Classes derived from this must always declare __slots__ (we use many
85 55224070 Guido Trotter
  config objects and the memory reduction is useful)
86 a8083063 Iustin Pop

87 a8083063 Iustin Pop
  """
88 a8083063 Iustin Pop
  __slots__ = []
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
  def __init__(self, **kwargs):
91 319856a9 Michael Hanselmann
    for k, v in kwargs.iteritems():
92 319856a9 Michael Hanselmann
      setattr(self, k, v)
93 560428be Guido Trotter
    self.UpgradeConfig()
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
  def __getattr__(self, name):
96 a8083063 Iustin Pop
    if name not in self.__slots__:
97 3ecf6786 Iustin Pop
      raise AttributeError("Invalid object attribute %s.%s" %
98 3ecf6786 Iustin Pop
                           (type(self).__name__, name))
99 a8083063 Iustin Pop
    return None
100 a8083063 Iustin Pop
101 47c28c5b Michael Hanselmann
  def __setitem__(self, key, value):
102 47c28c5b Michael Hanselmann
    if key not in self.__slots__:
103 3ecf6786 Iustin Pop
      raise KeyError(key)
104 47c28c5b Michael Hanselmann
    setattr(self, key, value)
105 47c28c5b Michael Hanselmann
106 a8083063 Iustin Pop
  def __getstate__(self):
107 a8083063 Iustin Pop
    state = {}
108 a8083063 Iustin Pop
    for name in self.__slots__:
109 a8083063 Iustin Pop
      if hasattr(self, name):
110 a8083063 Iustin Pop
        state[name] = getattr(self, name)
111 a8083063 Iustin Pop
    return state
112 a8083063 Iustin Pop
113 a8083063 Iustin Pop
  def __setstate__(self, state):
114 a8083063 Iustin Pop
    for name in state:
115 a8083063 Iustin Pop
      if name in self.__slots__:
116 a8083063 Iustin Pop
        setattr(self, name, state[name])
117 a8083063 Iustin Pop
118 ff9c047c Iustin Pop
  def ToDict(self):
119 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
120 ff9c047c Iustin Pop

121 ff9c047c Iustin Pop
    The generic routine just dumps all of this object's attributes in
122 ff9c047c Iustin Pop
    a dict. It does not work if the class has children who are
123 ff9c047c Iustin Pop
    ConfigObjects themselves (e.g. the nics list in an Instance), in
124 ff9c047c Iustin Pop
    which case the object should subclass the function in order to
125 ff9c047c Iustin Pop
    make sure all objects returned are only standard python types.
126 ff9c047c Iustin Pop

127 ff9c047c Iustin Pop
    """
128 ff9c047c Iustin Pop
    return dict([(k, getattr(self, k, None)) for k in self.__slots__])
129 ff9c047c Iustin Pop
130 ff9c047c Iustin Pop
  @classmethod
131 ff9c047c Iustin Pop
  def FromDict(cls, val):
132 ff9c047c Iustin Pop
    """Create an object from a dictionary.
133 ff9c047c Iustin Pop

134 ff9c047c Iustin Pop
    This generic routine takes a dict, instantiates a new instance of
135 ff9c047c Iustin Pop
    the given class, and sets attributes based on the dict content.
136 ff9c047c Iustin Pop

137 ff9c047c Iustin Pop
    As for `ToDict`, this does not work if the class has children
138 ff9c047c Iustin Pop
    who are ConfigObjects themselves (e.g. the nics list in an
139 ff9c047c Iustin Pop
    Instance), in which case the object should subclass the function
140 ff9c047c Iustin Pop
    and alter the objects.
141 ff9c047c Iustin Pop

142 ff9c047c Iustin Pop
    """
143 ff9c047c Iustin Pop
    if not isinstance(val, dict):
144 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
145 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
146 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
147 319856a9 Michael Hanselmann
    obj = cls(**val_str)
148 ff9c047c Iustin Pop
    return obj
149 ff9c047c Iustin Pop
150 ff9c047c Iustin Pop
  @staticmethod
151 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
152 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
153 ff9c047c Iustin Pop

154 ff9c047c Iustin Pop
    This method converts a container with elements derived from
155 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
156 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
157 ff9c047c Iustin Pop

158 ff9c047c Iustin Pop
    """
159 ff9c047c Iustin Pop
    if isinstance(container, dict):
160 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
161 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
162 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
163 ff9c047c Iustin Pop
    else:
164 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
165 ff9c047c Iustin Pop
                      type(container))
166 ff9c047c Iustin Pop
    return ret
167 ff9c047c Iustin Pop
168 ff9c047c Iustin Pop
  @staticmethod
169 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
170 ff9c047c Iustin Pop
    """Convert a container from standard python types.
171 ff9c047c Iustin Pop

172 ff9c047c Iustin Pop
    This method converts a container with standard python types to
173 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
174 ff9c047c Iustin Pop
    keys, only the values.
175 ff9c047c Iustin Pop

176 ff9c047c Iustin Pop
    """
177 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
178 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
179 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
180 ff9c047c Iustin Pop
    if c_type is dict:
181 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
182 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
183 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
184 ff9c047c Iustin Pop
    else:
185 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
186 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
187 ff9c047c Iustin Pop
    return ret
188 ff9c047c Iustin Pop
189 ff9c047c Iustin Pop
  def __repr__(self):
190 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
191 ff9c047c Iustin Pop
    return repr(self.ToDict())
192 ff9c047c Iustin Pop
193 560428be Guido Trotter
  def UpgradeConfig(self):
194 560428be Guido Trotter
    """Fill defaults for missing configuration values.
195 560428be Guido Trotter

196 560428be Guido Trotter
    This method will be called at object init time, and its implementation will
197 560428be Guido Trotter
    be object dependent.
198 560428be Guido Trotter

199 560428be Guido Trotter
    """
200 560428be Guido Trotter
    pass
201 560428be Guido Trotter
202 a8083063 Iustin Pop
203 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
204 5c947f38 Iustin Pop
  """An generic class supporting tags.
205 5c947f38 Iustin Pop

206 5c947f38 Iustin Pop
  """
207 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
208 2057f6c7 Iustin Pop
209 5c947f38 Iustin Pop
  @staticmethod
210 5c947f38 Iustin Pop
  def ValidateTag(tag):
211 5c947f38 Iustin Pop
    """Check if a tag is valid.
212 5c947f38 Iustin Pop

213 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
214 5c947f38 Iustin Pop
    function has no return value.
215 5c947f38 Iustin Pop

216 5c947f38 Iustin Pop
    """
217 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
218 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
219 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
220 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
221 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
222 5c947f38 Iustin Pop
    if not tag:
223 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
224 28ab6fed Iustin Pop
    if not re.match("^[\w.+*/:-]+$", tag):
225 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
226 5c947f38 Iustin Pop
227 5c947f38 Iustin Pop
  def GetTags(self):
228 5c947f38 Iustin Pop
    """Return the tags list.
229 5c947f38 Iustin Pop

230 5c947f38 Iustin Pop
    """
231 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
232 5c947f38 Iustin Pop
    if tags is None:
233 5c947f38 Iustin Pop
      tags = self.tags = set()
234 5c947f38 Iustin Pop
    return tags
235 5c947f38 Iustin Pop
236 5c947f38 Iustin Pop
  def AddTag(self, tag):
237 5c947f38 Iustin Pop
    """Add a new tag.
238 5c947f38 Iustin Pop

239 5c947f38 Iustin Pop
    """
240 5c947f38 Iustin Pop
    self.ValidateTag(tag)
241 5c947f38 Iustin Pop
    tags = self.GetTags()
242 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
243 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
244 5c947f38 Iustin Pop
    self.GetTags().add(tag)
245 5c947f38 Iustin Pop
246 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
247 5c947f38 Iustin Pop
    """Remove a tag.
248 5c947f38 Iustin Pop

249 5c947f38 Iustin Pop
    """
250 5c947f38 Iustin Pop
    self.ValidateTag(tag)
251 5c947f38 Iustin Pop
    tags = self.GetTags()
252 5c947f38 Iustin Pop
    try:
253 5c947f38 Iustin Pop
      tags.remove(tag)
254 5c947f38 Iustin Pop
    except KeyError:
255 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
256 5c947f38 Iustin Pop
257 ff9c047c Iustin Pop
  def ToDict(self):
258 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
259 ff9c047c Iustin Pop

260 ff9c047c Iustin Pop
    This replaces the tags set with a list.
261 ff9c047c Iustin Pop

262 ff9c047c Iustin Pop
    """
263 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
264 ff9c047c Iustin Pop
265 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
266 ff9c047c Iustin Pop
    if isinstance(tags, set):
267 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
268 ff9c047c Iustin Pop
    return bo
269 ff9c047c Iustin Pop
270 ff9c047c Iustin Pop
  @classmethod
271 ff9c047c Iustin Pop
  def FromDict(cls, val):
272 ff9c047c Iustin Pop
    """Custom function for instances.
273 ff9c047c Iustin Pop

274 ff9c047c Iustin Pop
    """
275 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
276 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
277 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
278 ff9c047c Iustin Pop
    return obj
279 ff9c047c Iustin Pop
280 5c947f38 Iustin Pop
281 a8083063 Iustin Pop
class ConfigData(ConfigObject):
282 a8083063 Iustin Pop
  """Top-level config object."""
283 f6bd6e98 Michael Hanselmann
  __slots__ = ["version", "cluster", "nodes", "instances", "serial_no"]
284 a8083063 Iustin Pop
285 ff9c047c Iustin Pop
  def ToDict(self):
286 ff9c047c Iustin Pop
    """Custom function for top-level config data.
287 ff9c047c Iustin Pop

288 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
289 ff9c047c Iustin Pop
    with standard python types.
290 ff9c047c Iustin Pop

291 ff9c047c Iustin Pop
    """
292 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
293 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
294 ff9c047c Iustin Pop
    for key in "nodes", "instances":
295 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
296 ff9c047c Iustin Pop
297 ff9c047c Iustin Pop
    return mydict
298 ff9c047c Iustin Pop
299 ff9c047c Iustin Pop
  @classmethod
300 ff9c047c Iustin Pop
  def FromDict(cls, val):
301 ff9c047c Iustin Pop
    """Custom function for top-level config data
302 ff9c047c Iustin Pop

303 ff9c047c Iustin Pop
    """
304 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
305 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
306 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
307 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
308 ff9c047c Iustin Pop
    return obj
309 ff9c047c Iustin Pop
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
class NIC(ConfigObject):
312 a8083063 Iustin Pop
  """Config object representing a network card."""
313 13f1af63 Guido Trotter
  __slots__ = ["mac", "ip", "bridge", "nicparams"]
314 a8083063 Iustin Pop
315 255e19d4 Guido Trotter
  @classmethod
316 255e19d4 Guido Trotter
  def CheckParameterSyntax(cls, nicparams):
317 255e19d4 Guido Trotter
    """Check the given parameters for validity.
318 255e19d4 Guido Trotter

319 255e19d4 Guido Trotter
    @type nicparams:  dict
320 255e19d4 Guido Trotter
    @param nicparams: dictionary with parameter names/value
321 255e19d4 Guido Trotter
    @raise errors.ConfigurationError: when a parameter is not valid
322 255e19d4 Guido Trotter

323 255e19d4 Guido Trotter
    """
324 255e19d4 Guido Trotter
    if nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES:
325 255e19d4 Guido Trotter
      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
326 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
327 255e19d4 Guido Trotter
328 255e19d4 Guido Trotter
    if (nicparams[constants.NIC_MODE] is constants.NIC_MODE_BRIDGED and
329 255e19d4 Guido Trotter
        not nicparams[constants.NIC_LINK]):
330 255e19d4 Guido Trotter
      err = "Missing bridged nic link"
331 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
332 255e19d4 Guido Trotter
333 13f1af63 Guido Trotter
  def UpgradeConfig(self):
334 13f1af63 Guido Trotter
    """Fill defaults for missing configuration values.
335 13f1af63 Guido Trotter

336 13f1af63 Guido Trotter
    """
337 13f1af63 Guido Trotter
    if self.nicparams is None:
338 13f1af63 Guido Trotter
      self.nicparams = {}
339 13f1af63 Guido Trotter
      if self.bridge is not None:
340 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_MODE] = constants.NIC_MODE_BRIDGED
341 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_LINK] = self.bridge
342 13f1af63 Guido Trotter
343 a8083063 Iustin Pop
344 a8083063 Iustin Pop
class Disk(ConfigObject):
345 a8083063 Iustin Pop
  """Config object representing a block device."""
346 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
347 08db7c5c Iustin Pop
               "children", "iv_name", "size", "mode"]
348 a8083063 Iustin Pop
349 a8083063 Iustin Pop
  def CreateOnSecondary(self):
350 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
351 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
354 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
355 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
  def OpenOnSecondary(self):
358 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
359 fe96220b Iustin Pop
    return self.dev_type in (constants.LD_LV,)
360 a8083063 Iustin Pop
361 222f2dd5 Iustin Pop
  def StaticDevPath(self):
362 222f2dd5 Iustin Pop
    """Return the device path if this device type has a static one.
363 222f2dd5 Iustin Pop

364 222f2dd5 Iustin Pop
    Some devices (LVM for example) live always at the same /dev/ path,
365 222f2dd5 Iustin Pop
    irrespective of their status. For such devices, we return this
366 222f2dd5 Iustin Pop
    path, for others we return None.
367 222f2dd5 Iustin Pop

368 222f2dd5 Iustin Pop
    """
369 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
370 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
371 222f2dd5 Iustin Pop
    return None
372 222f2dd5 Iustin Pop
373 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
374 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
375 fc1dc9d7 Iustin Pop

376 fc1dc9d7 Iustin Pop
    This method will return either -1 (all children) or a positive
377 fc1dc9d7 Iustin Pop
    number denoting the minimum number of children needed for
378 fc1dc9d7 Iustin Pop
    activation (only mirrored devices will usually return >=0).
379 fc1dc9d7 Iustin Pop

380 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
381 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
382 fc1dc9d7 Iustin Pop
    -1.
383 fc1dc9d7 Iustin Pop

384 fc1dc9d7 Iustin Pop
    """
385 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
386 fc1dc9d7 Iustin Pop
      return 0
387 fc1dc9d7 Iustin Pop
    return -1
388 fc1dc9d7 Iustin Pop
389 a8083063 Iustin Pop
  def GetNodes(self, node):
390 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
391 a8083063 Iustin Pop

392 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
393 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
394 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
395 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
    """
398 00fb8246 Michael Hanselmann
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
399 a8083063 Iustin Pop
      result = [node]
400 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
401 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
402 a8083063 Iustin Pop
      if node not in result:
403 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
404 a8083063 Iustin Pop
    else:
405 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
406 a8083063 Iustin Pop
    return result
407 a8083063 Iustin Pop
408 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
409 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
410 a8083063 Iustin Pop

411 a8083063 Iustin Pop
    This method, given the node on which the parent disk lives, will
412 a8083063 Iustin Pop
    return the list of all (node, disk) pairs which describe the disk
413 abdf0113 Iustin Pop
    tree in the most compact way. For example, a drbd/lvm stack
414 abdf0113 Iustin Pop
    will be returned as (primary_node, drbd) and (secondary_node, drbd)
415 abdf0113 Iustin Pop
    which represents all the top-level devices on the nodes.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
    """
418 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
419 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
420 a8083063 Iustin Pop
    if not self.children:
421 a8083063 Iustin Pop
      # leaf device
422 a8083063 Iustin Pop
      return result
423 a8083063 Iustin Pop
    for node in my_nodes:
424 a8083063 Iustin Pop
      for child in self.children:
425 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
426 a8083063 Iustin Pop
        if len(child_result) == 1:
427 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
428 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
429 a8083063 Iustin Pop
          # own entry for this node describes it completely
430 a8083063 Iustin Pop
          continue
431 a8083063 Iustin Pop
        else:
432 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
433 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
434 a8083063 Iustin Pop
          # one of its descendants
435 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
436 a8083063 Iustin Pop
            if subnode not in my_nodes:
437 a8083063 Iustin Pop
              result.append((subnode, subdisk))
438 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
439 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
440 a8083063 Iustin Pop
            # be different)
441 a8083063 Iustin Pop
    return result
442 a8083063 Iustin Pop
443 acec9d51 Iustin Pop
  def RecordGrow(self, amount):
444 acec9d51 Iustin Pop
    """Update the size of this disk after growth.
445 acec9d51 Iustin Pop

446 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
447 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
448 acec9d51 Iustin Pop
    actual algorithms from bdev.
449 acec9d51 Iustin Pop

450 acec9d51 Iustin Pop
    """
451 acec9d51 Iustin Pop
    if self.dev_type == constants.LD_LV:
452 acec9d51 Iustin Pop
      self.size += amount
453 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
454 acec9d51 Iustin Pop
      if self.children:
455 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
456 acec9d51 Iustin Pop
      self.size += amount
457 acec9d51 Iustin Pop
    else:
458 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
459 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
460 acec9d51 Iustin Pop
461 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
462 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
463 0402302c Iustin Pop

464 0402302c Iustin Pop
    This is used only for drbd, which needs ip/port configuration.
465 0402302c Iustin Pop

466 0402302c Iustin Pop
    The routine descends down and updates its children also, because
467 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
468 0402302c Iustin Pop
    node.
469 0402302c Iustin Pop

470 0402302c Iustin Pop
    Arguments:
471 0402302c Iustin Pop
      - target_node: the node we wish to configure for
472 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
473 0402302c Iustin Pop

474 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
475 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
476 0402302c Iustin Pop
    in the disk tree.
477 0402302c Iustin Pop

478 0402302c Iustin Pop
    """
479 0402302c Iustin Pop
    if self.children:
480 0402302c Iustin Pop
      for child in self.children:
481 0402302c Iustin Pop
        child.SetPhysicalID(target_node, nodes_ip)
482 0402302c Iustin Pop
483 0402302c Iustin Pop
    if self.logical_id is None and self.physical_id is not None:
484 0402302c Iustin Pop
      return
485 0402302c Iustin Pop
    if self.dev_type in constants.LDS_DRBD:
486 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = self.logical_id
487 0402302c Iustin Pop
      if target_node not in (pnode, snode):
488 0402302c Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
489 0402302c Iustin Pop
                                        target_node)
490 0402302c Iustin Pop
      pnode_ip = nodes_ip.get(pnode, None)
491 0402302c Iustin Pop
      snode_ip = nodes_ip.get(snode, None)
492 0402302c Iustin Pop
      if pnode_ip is None or snode_ip is None:
493 0402302c Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
494 0402302c Iustin Pop
                                        " for %s" % str(self))
495 ffa1c0dc Iustin Pop
      p_data = (pnode_ip, port)
496 ffa1c0dc Iustin Pop
      s_data = (snode_ip, port)
497 0402302c Iustin Pop
      if pnode == target_node:
498 f9518d38 Iustin Pop
        self.physical_id = p_data + s_data + (pminor, secret)
499 0402302c Iustin Pop
      else: # it must be secondary, we tested above
500 f9518d38 Iustin Pop
        self.physical_id = s_data + p_data + (sminor, secret)
501 0402302c Iustin Pop
    else:
502 0402302c Iustin Pop
      self.physical_id = self.logical_id
503 0402302c Iustin Pop
    return
504 0402302c Iustin Pop
505 ff9c047c Iustin Pop
  def ToDict(self):
506 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
507 ff9c047c Iustin Pop

508 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
509 ff9c047c Iustin Pop
    standard python types.
510 ff9c047c Iustin Pop

511 ff9c047c Iustin Pop
    """
512 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
513 ff9c047c Iustin Pop
514 ff9c047c Iustin Pop
    for attr in ("children",):
515 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
516 ff9c047c Iustin Pop
      if alist:
517 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
518 ff9c047c Iustin Pop
    return bo
519 ff9c047c Iustin Pop
520 ff9c047c Iustin Pop
  @classmethod
521 ff9c047c Iustin Pop
  def FromDict(cls, val):
522 ff9c047c Iustin Pop
    """Custom function for Disks
523 ff9c047c Iustin Pop

524 ff9c047c Iustin Pop
    """
525 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
526 ff9c047c Iustin Pop
    if obj.children:
527 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
528 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
529 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
530 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
531 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
532 f9518d38 Iustin Pop
    if obj.dev_type in constants.LDS_DRBD:
533 f9518d38 Iustin Pop
      # we need a tuple of length six here
534 f9518d38 Iustin Pop
      if len(obj.logical_id) < 6:
535 f9518d38 Iustin Pop
        obj.logical_id += (None,) * (6 - len(obj.logical_id))
536 ff9c047c Iustin Pop
    return obj
537 ff9c047c Iustin Pop
538 65a15336 Iustin Pop
  def __str__(self):
539 65a15336 Iustin Pop
    """Custom str() formatter for disks.
540 65a15336 Iustin Pop

541 65a15336 Iustin Pop
    """
542 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
543 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
544 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
545 89f28b76 Iustin Pop
      node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
546 00fb8246 Michael Hanselmann
      val = "<DRBD8("
547 073ca59e Iustin Pop
      if self.physical_id is None:
548 073ca59e Iustin Pop
        phy = "unconfigured"
549 073ca59e Iustin Pop
      else:
550 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
551 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
552 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
553 073ca59e Iustin Pop
554 89f28b76 Iustin Pop
      val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
555 89f28b76 Iustin Pop
              (node_a, minor_a, node_b, minor_b, port, phy))
556 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
557 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
558 65a15336 Iustin Pop
      else:
559 65a15336 Iustin Pop
        val += "no local storage"
560 65a15336 Iustin Pop
    else:
561 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
562 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
563 65a15336 Iustin Pop
    if self.iv_name is None:
564 65a15336 Iustin Pop
      val += ", not visible"
565 65a15336 Iustin Pop
    else:
566 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
567 fd965830 Iustin Pop
    if isinstance(self.size, int):
568 fd965830 Iustin Pop
      val += ", size=%dm)>" % self.size
569 fd965830 Iustin Pop
    else:
570 fd965830 Iustin Pop
      val += ", size='%s')>" % (self.size,)
571 65a15336 Iustin Pop
    return val
572 65a15336 Iustin Pop
573 332d0e37 Iustin Pop
  def Verify(self):
574 332d0e37 Iustin Pop
    """Checks that this disk is correctly configured.
575 332d0e37 Iustin Pop

576 332d0e37 Iustin Pop
    """
577 332d0e37 Iustin Pop
    errors = []
578 332d0e37 Iustin Pop
    if self.mode not in constants.DISK_ACCESS_SET:
579 332d0e37 Iustin Pop
      errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
580 332d0e37 Iustin Pop
    return errors
581 332d0e37 Iustin Pop
582 a8083063 Iustin Pop
583 ec29fe40 Iustin Pop
class Instance(TaggableObject):
584 a8083063 Iustin Pop
  """Config object representing an instance."""
585 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
586 a8083063 Iustin Pop
    "name",
587 a8083063 Iustin Pop
    "primary_node",
588 a8083063 Iustin Pop
    "os",
589 e69d05fd Iustin Pop
    "hypervisor",
590 5bf7b5cf Iustin Pop
    "hvparams",
591 5bf7b5cf Iustin Pop
    "beparams",
592 0d68c45d Iustin Pop
    "admin_up",
593 a8083063 Iustin Pop
    "nics",
594 a8083063 Iustin Pop
    "disks",
595 a8083063 Iustin Pop
    "disk_template",
596 58acb49d Alexander Schreiber
    "network_port",
597 be1fa613 Iustin Pop
    "serial_no",
598 a8083063 Iustin Pop
    ]
599 a8083063 Iustin Pop
600 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
601 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
602 a8083063 Iustin Pop

603 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
604 cfcc5c6d Iustin Pop

605 cfcc5c6d Iustin Pop
    """
606 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
607 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
608 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
609 cfcc5c6d Iustin Pop
610 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
611 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
612 cfcc5c6d Iustin Pop
613 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
614 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
615 cfcc5c6d Iustin Pop

616 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
617 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
618 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
619 a8083063 Iustin Pop
    dynamically.
620 a8083063 Iustin Pop

621 a8083063 Iustin Pop
    """
622 cfcc5c6d Iustin Pop
    def _Helper(nodes, device):
623 cfcc5c6d Iustin Pop
      """Recursively computes nodes given a top device."""
624 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
625 cfcc5c6d Iustin Pop
        nodea, nodeb = device.logical_id[:2]
626 cfcc5c6d Iustin Pop
        nodes.add(nodea)
627 cfcc5c6d Iustin Pop
        nodes.add(nodeb)
628 a8083063 Iustin Pop
      if device.children:
629 a8083063 Iustin Pop
        for child in device.children:
630 cfcc5c6d Iustin Pop
          _Helper(nodes, child)
631 a8083063 Iustin Pop
632 cfcc5c6d Iustin Pop
    all_nodes = set()
633 99c7b2a1 Iustin Pop
    all_nodes.add(self.primary_node)
634 a8083063 Iustin Pop
    for device in self.disks:
635 cfcc5c6d Iustin Pop
      _Helper(all_nodes, device)
636 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
637 a8083063 Iustin Pop
638 cfcc5c6d Iustin Pop
  all_nodes = property(_ComputeAllNodes, None, None,
639 cfcc5c6d Iustin Pop
                       "List of all nodes of the instance")
640 a8083063 Iustin Pop
641 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
642 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
643 a8083063 Iustin Pop

644 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
645 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
646 a8083063 Iustin Pop

647 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
648 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
649 a8083063 Iustin Pop

650 c41eea6e Iustin Pop
    @return: None if lvmap arg is given, otherwise, a dictionary
651 c41eea6e Iustin Pop
        of the form { 'nodename' : ['volume1', 'volume2', ...], ... }
652 a8083063 Iustin Pop

653 a8083063 Iustin Pop
    """
654 a8083063 Iustin Pop
    if node == None:
655 a8083063 Iustin Pop
      node = self.primary_node
656 a8083063 Iustin Pop
657 a8083063 Iustin Pop
    if lvmap is None:
658 a8083063 Iustin Pop
      lvmap = { node : [] }
659 a8083063 Iustin Pop
      ret = lvmap
660 a8083063 Iustin Pop
    else:
661 a8083063 Iustin Pop
      if not node in lvmap:
662 a8083063 Iustin Pop
        lvmap[node] = []
663 a8083063 Iustin Pop
      ret = None
664 a8083063 Iustin Pop
665 a8083063 Iustin Pop
    if not devs:
666 a8083063 Iustin Pop
      devs = self.disks
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
    for dev in devs:
669 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
670 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
671 a8083063 Iustin Pop
672 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
673 a8083063 Iustin Pop
        if dev.children:
674 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
675 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
676 a8083063 Iustin Pop
677 a8083063 Iustin Pop
      elif dev.children:
678 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
679 a8083063 Iustin Pop
680 a8083063 Iustin Pop
    return ret
681 a8083063 Iustin Pop
682 ad24e046 Iustin Pop
  def FindDisk(self, idx):
683 ad24e046 Iustin Pop
    """Find a disk given having a specified index.
684 644eeef9 Iustin Pop

685 ad24e046 Iustin Pop
    This is just a wrapper that does validation of the index.
686 644eeef9 Iustin Pop

687 ad24e046 Iustin Pop
    @type idx: int
688 ad24e046 Iustin Pop
    @param idx: the disk index
689 ad24e046 Iustin Pop
    @rtype: L{Disk}
690 ad24e046 Iustin Pop
    @return: the corresponding disk
691 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
692 644eeef9 Iustin Pop

693 ad24e046 Iustin Pop
    """
694 ad24e046 Iustin Pop
    try:
695 ad24e046 Iustin Pop
      idx = int(idx)
696 ad24e046 Iustin Pop
      return self.disks[idx]
697 ad24e046 Iustin Pop
    except ValueError, err:
698 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err))
699 ad24e046 Iustin Pop
    except IndexError:
700 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
701 ad24e046 Iustin Pop
                                 " 0 to %d" % (idx, len(self.disks)))
702 644eeef9 Iustin Pop
703 ff9c047c Iustin Pop
  def ToDict(self):
704 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
705 ff9c047c Iustin Pop

706 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
707 ff9c047c Iustin Pop
    python types.
708 ff9c047c Iustin Pop

709 ff9c047c Iustin Pop
    """
710 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
711 ff9c047c Iustin Pop
712 ff9c047c Iustin Pop
    for attr in "nics", "disks":
713 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
714 ff9c047c Iustin Pop
      if alist:
715 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
716 ff9c047c Iustin Pop
      else:
717 ff9c047c Iustin Pop
        nlist = []
718 ff9c047c Iustin Pop
      bo[attr] = nlist
719 ff9c047c Iustin Pop
    return bo
720 ff9c047c Iustin Pop
721 ff9c047c Iustin Pop
  @classmethod
722 ff9c047c Iustin Pop
  def FromDict(cls, val):
723 ff9c047c Iustin Pop
    """Custom function for instances.
724 ff9c047c Iustin Pop

725 ff9c047c Iustin Pop
    """
726 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
727 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
728 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
729 ff9c047c Iustin Pop
    return obj
730 ff9c047c Iustin Pop
731 a8083063 Iustin Pop
732 a8083063 Iustin Pop
class OS(ConfigObject):
733 a8083063 Iustin Pop
  """Config object representing an operating system."""
734 a8083063 Iustin Pop
  __slots__ = [
735 a8083063 Iustin Pop
    "name",
736 a8083063 Iustin Pop
    "path",
737 37482e7b Guido Trotter
    "status",
738 082a7f91 Guido Trotter
    "api_versions",
739 a8083063 Iustin Pop
    "create_script",
740 a8083063 Iustin Pop
    "export_script",
741 386b57af Iustin Pop
    "import_script",
742 386b57af Iustin Pop
    "rename_script",
743 a8083063 Iustin Pop
    ]
744 a8083063 Iustin Pop
745 d2c807e4 Guido Trotter
  @classmethod
746 d2c807e4 Guido Trotter
  def FromInvalidOS(cls, err):
747 d2c807e4 Guido Trotter
    """Create an OS from an InvalidOS error.
748 d2c807e4 Guido Trotter

749 d2c807e4 Guido Trotter
    This routine knows how to convert an InvalidOS error to an OS
750 d2c807e4 Guido Trotter
    object representing the broken OS with a meaningful error message.
751 d2c807e4 Guido Trotter

752 d2c807e4 Guido Trotter
    """
753 d2c807e4 Guido Trotter
    if not isinstance(err, errors.InvalidOS):
754 d2c807e4 Guido Trotter
      raise errors.ProgrammerError("Trying to initialize an OS from an"
755 d2c807e4 Guido Trotter
                                   " invalid object of type %s" % type(err))
756 d2c807e4 Guido Trotter
757 d2c807e4 Guido Trotter
    return cls(name=err.args[0], path=err.args[1], status=err.args[2])
758 d2c807e4 Guido Trotter
759 37482e7b Guido Trotter
  def __nonzero__(self):
760 37482e7b Guido Trotter
    return self.status == constants.OS_VALID_STATUS
761 37482e7b Guido Trotter
762 37482e7b Guido Trotter
  __bool__ = __nonzero__
763 a8083063 Iustin Pop
764 7c0d6283 Michael Hanselmann
765 ec29fe40 Iustin Pop
class Node(TaggableObject):
766 a8083063 Iustin Pop
  """Config object representing a node."""
767 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
768 ec29fe40 Iustin Pop
    "name",
769 ec29fe40 Iustin Pop
    "primary_ip",
770 ec29fe40 Iustin Pop
    "secondary_ip",
771 be1fa613 Iustin Pop
    "serial_no",
772 8b8b8b81 Iustin Pop
    "master_candidate",
773 fc0fe88c Iustin Pop
    "offline",
774 af64c0ea Iustin Pop
    "drained",
775 ec29fe40 Iustin Pop
    ]
776 a8083063 Iustin Pop
777 a8083063 Iustin Pop
778 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
779 a8083063 Iustin Pop
  """Config object representing the cluster."""
780 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
781 a8083063 Iustin Pop
    "serial_no",
782 a8083063 Iustin Pop
    "rsahostkeypub",
783 a8083063 Iustin Pop
    "highest_used_port",
784 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
785 a8083063 Iustin Pop
    "mac_prefix",
786 a8083063 Iustin Pop
    "volume_group_name",
787 a8083063 Iustin Pop
    "default_bridge",
788 02691904 Alexander Schreiber
    "default_hypervisor",
789 f6bd6e98 Michael Hanselmann
    "master_node",
790 f6bd6e98 Michael Hanselmann
    "master_ip",
791 f6bd6e98 Michael Hanselmann
    "master_netdev",
792 f6bd6e98 Michael Hanselmann
    "cluster_name",
793 f6bd6e98 Michael Hanselmann
    "file_storage_dir",
794 e69d05fd Iustin Pop
    "enabled_hypervisors",
795 5bf7b5cf Iustin Pop
    "hvparams",
796 5bf7b5cf Iustin Pop
    "beparams",
797 c8fcde47 Guido Trotter
    "nicparams",
798 4b7735f9 Iustin Pop
    "candidate_pool_size",
799 b86a6bcd Guido Trotter
    "modify_etc_hosts",
800 a8083063 Iustin Pop
    ]
801 a8083063 Iustin Pop
802 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
803 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
804 b86a6bcd Guido Trotter

805 b86a6bcd Guido Trotter
    """
806 c1b42c18 Guido Trotter
    if self.hvparams is None:
807 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
808 c1b42c18 Guido Trotter
    else:
809 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
810 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
811 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
812 c1b42c18 Guido Trotter
813 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
814 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
815 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
816 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
817 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
818 c8fcde47 Guido Trotter
    if migrate_default_bridge:
819 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
820 c8fcde47 Guido Trotter
        self.default_bridge
821 c1b42c18 Guido Trotter
822 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
823 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
824 b86a6bcd Guido Trotter
825 319856a9 Michael Hanselmann
  def ToDict(self):
826 319856a9 Michael Hanselmann
    """Custom function for cluster.
827 319856a9 Michael Hanselmann

828 319856a9 Michael Hanselmann
    """
829 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
830 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
831 319856a9 Michael Hanselmann
    return mydict
832 319856a9 Michael Hanselmann
833 319856a9 Michael Hanselmann
  @classmethod
834 319856a9 Michael Hanselmann
  def FromDict(cls, val):
835 319856a9 Michael Hanselmann
    """Custom function for cluster.
836 319856a9 Michael Hanselmann

837 319856a9 Michael Hanselmann
    """
838 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
839 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
840 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
841 319856a9 Michael Hanselmann
    return obj
842 319856a9 Michael Hanselmann
843 5bf7b5cf Iustin Pop
  def FillHV(self, instance):
844 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
845 5bf7b5cf Iustin Pop

846 5bf7b5cf Iustin Pop
    @type instance: object
847 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
848 5bf7b5cf Iustin Pop
    @rtype: dict
849 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
850 5bf7b5cf Iustin Pop
        the cluster defaults
851 5bf7b5cf Iustin Pop

852 5bf7b5cf Iustin Pop
    """
853 abe609b2 Guido Trotter
    return FillDict(self.hvparams.get(instance.hypervisor, {}),
854 5bf7b5cf Iustin Pop
                         instance.hvparams)
855 5bf7b5cf Iustin Pop
856 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
857 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
858 5bf7b5cf Iustin Pop

859 5bf7b5cf Iustin Pop
    @type instance: object
860 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
861 5bf7b5cf Iustin Pop
    @rtype: dict
862 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
863 5bf7b5cf Iustin Pop
        the cluster defaults
864 5bf7b5cf Iustin Pop

865 5bf7b5cf Iustin Pop
    """
866 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
867 4ef7f423 Guido Trotter
                          instance.beparams)
868 5bf7b5cf Iustin Pop
869 5c947f38 Iustin Pop
870 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
871 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
872 a8083063 Iustin Pop

873 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
874 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
875 a8083063 Iustin Pop
  buffer.
876 a8083063 Iustin Pop

877 a8083063 Iustin Pop
  """
878 a8083063 Iustin Pop
  def Dumps(self):
879 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
880 a8083063 Iustin Pop
    buf = StringIO()
881 a8083063 Iustin Pop
    self.write(buf)
882 a8083063 Iustin Pop
    return buf.getvalue()
883 a8083063 Iustin Pop
884 a8083063 Iustin Pop
  @staticmethod
885 a8083063 Iustin Pop
  def Loads(data):
886 a8083063 Iustin Pop
    """Load data from a string."""
887 a8083063 Iustin Pop
    buf = StringIO(data)
888 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
889 a8083063 Iustin Pop
    cfp.readfp(buf)
890 a8083063 Iustin Pop
    return cfp