Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ f208978a

History | View | Annotate | Download (26.7 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 96acbc09 Michael Hanselmann
43 abe609b2 Guido Trotter
def FillDict(defaults_dict, custom_dict):
44 29921401 Iustin Pop
  """Basic function to apply settings on top a default dict.
45 abe609b2 Guido Trotter

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

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

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

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

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

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

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

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

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

121 ff9c047c Iustin Pop
    """
122 4c14965f Guido Trotter
    result = {}
123 4c14965f Guido Trotter
    for name in self.__slots__:
124 4c14965f Guido Trotter
      value = getattr(self, name, None)
125 4c14965f Guido Trotter
      if value is not None:
126 4c14965f Guido Trotter
        result[name] = value
127 4c14965f Guido Trotter
    return result
128 4c14965f Guido Trotter
129 4c14965f Guido Trotter
  __getstate__ = ToDict
130 ff9c047c Iustin Pop
131 ff9c047c Iustin Pop
  @classmethod
132 ff9c047c Iustin Pop
  def FromDict(cls, val):
133 ff9c047c Iustin Pop
    """Create an object from a dictionary.
134 ff9c047c Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

385 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
386 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
387 fc1dc9d7 Iustin Pop
    -1.
388 fc1dc9d7 Iustin Pop

389 fc1dc9d7 Iustin Pop
    """
390 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
391 fc1dc9d7 Iustin Pop
      return 0
392 fc1dc9d7 Iustin Pop
    return -1
393 fc1dc9d7 Iustin Pop
394 a8083063 Iustin Pop
  def GetNodes(self, node):
395 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
396 a8083063 Iustin Pop

397 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
398 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
399 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
400 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
401 a8083063 Iustin Pop

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

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

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

451 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
452 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
453 acec9d51 Iustin Pop
    actual algorithms from bdev.
454 acec9d51 Iustin Pop

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

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

471 0402302c Iustin Pop
    The routine descends down and updates its children also, because
472 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
473 0402302c Iustin Pop
    node.
474 0402302c Iustin Pop

475 0402302c Iustin Pop
    Arguments:
476 0402302c Iustin Pop
      - target_node: the node we wish to configure for
477 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
478 0402302c Iustin Pop

479 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
480 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
481 0402302c Iustin Pop
    in the disk tree.
482 0402302c Iustin Pop

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

513 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
514 ff9c047c Iustin Pop
    standard python types.
515 ff9c047c Iustin Pop

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

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

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

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

608 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
609 cfcc5c6d Iustin Pop

610 cfcc5c6d Iustin Pop
    """
611 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
612 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
613 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
614 cfcc5c6d Iustin Pop
615 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
616 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
617 cfcc5c6d Iustin Pop
618 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
619 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
620 cfcc5c6d Iustin Pop

621 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
622 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
623 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
624 a8083063 Iustin Pop
    dynamically.
625 a8083063 Iustin Pop

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

649 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
650 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
651 a8083063 Iustin Pop

652 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
653 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
654 a8083063 Iustin Pop

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

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

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

692 ad24e046 Iustin Pop
    @type idx: int
693 ad24e046 Iustin Pop
    @param idx: the disk index
694 ad24e046 Iustin Pop
    @rtype: L{Disk}
695 ad24e046 Iustin Pop
    @return: the corresponding disk
696 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
697 644eeef9 Iustin Pop

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

711 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
712 ff9c047c Iustin Pop
    python types.
713 ff9c047c Iustin Pop

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

730 ff9c047c Iustin Pop
    """
731 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
732 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
733 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
734 ff9c047c Iustin Pop
    return obj
735 ff9c047c Iustin Pop
736 a8083063 Iustin Pop
737 a8083063 Iustin Pop
class OS(ConfigObject):
738 a8083063 Iustin Pop
  """Config object representing an operating system."""
739 a8083063 Iustin Pop
  __slots__ = [
740 a8083063 Iustin Pop
    "name",
741 a8083063 Iustin Pop
    "path",
742 082a7f91 Guido Trotter
    "api_versions",
743 a8083063 Iustin Pop
    "create_script",
744 a8083063 Iustin Pop
    "export_script",
745 386b57af Iustin Pop
    "import_script",
746 386b57af Iustin Pop
    "rename_script",
747 a8083063 Iustin Pop
    ]
748 a8083063 Iustin Pop
749 7c0d6283 Michael Hanselmann
750 ec29fe40 Iustin Pop
class Node(TaggableObject):
751 a8083063 Iustin Pop
  """Config object representing a node."""
752 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
753 ec29fe40 Iustin Pop
    "name",
754 ec29fe40 Iustin Pop
    "primary_ip",
755 ec29fe40 Iustin Pop
    "secondary_ip",
756 be1fa613 Iustin Pop
    "serial_no",
757 8b8b8b81 Iustin Pop
    "master_candidate",
758 fc0fe88c Iustin Pop
    "offline",
759 af64c0ea Iustin Pop
    "drained",
760 ec29fe40 Iustin Pop
    ]
761 a8083063 Iustin Pop
762 a8083063 Iustin Pop
763 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
764 a8083063 Iustin Pop
  """Config object representing the cluster."""
765 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
766 a8083063 Iustin Pop
    "serial_no",
767 a8083063 Iustin Pop
    "rsahostkeypub",
768 a8083063 Iustin Pop
    "highest_used_port",
769 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
770 a8083063 Iustin Pop
    "mac_prefix",
771 a8083063 Iustin Pop
    "volume_group_name",
772 a8083063 Iustin Pop
    "default_bridge",
773 02691904 Alexander Schreiber
    "default_hypervisor",
774 f6bd6e98 Michael Hanselmann
    "master_node",
775 f6bd6e98 Michael Hanselmann
    "master_ip",
776 f6bd6e98 Michael Hanselmann
    "master_netdev",
777 f6bd6e98 Michael Hanselmann
    "cluster_name",
778 f6bd6e98 Michael Hanselmann
    "file_storage_dir",
779 e69d05fd Iustin Pop
    "enabled_hypervisors",
780 5bf7b5cf Iustin Pop
    "hvparams",
781 5bf7b5cf Iustin Pop
    "beparams",
782 c8fcde47 Guido Trotter
    "nicparams",
783 4b7735f9 Iustin Pop
    "candidate_pool_size",
784 b86a6bcd Guido Trotter
    "modify_etc_hosts",
785 a8083063 Iustin Pop
    ]
786 a8083063 Iustin Pop
787 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
788 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
789 b86a6bcd Guido Trotter

790 b86a6bcd Guido Trotter
    """
791 c1b42c18 Guido Trotter
    if self.hvparams is None:
792 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
793 c1b42c18 Guido Trotter
    else:
794 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
795 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
796 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
797 c1b42c18 Guido Trotter
798 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
799 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
800 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
801 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
802 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
803 c8fcde47 Guido Trotter
    if migrate_default_bridge:
804 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
805 c8fcde47 Guido Trotter
        self.default_bridge
806 c1b42c18 Guido Trotter
807 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
808 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
809 b86a6bcd Guido Trotter
810 9b31ca85 Guido Trotter
    # default_bridge is no longer used it 2.1. The slot is left there to
811 9b31ca85 Guido Trotter
    # support auto-upgrading, but will be removed in 2.2
812 9b31ca85 Guido Trotter
    if self.default_bridge is not None:
813 9b31ca85 Guido Trotter
      self.default_bridge = None
814 9b31ca85 Guido Trotter
815 066f465d Guido Trotter
    # default_hypervisor is just the first enabled one in 2.1
816 066f465d Guido Trotter
    if self.default_hypervisor is not None:
817 066f465d Guido Trotter
      self.enabled_hypervisors = [self.default_hypervisor] + \
818 066f465d Guido Trotter
        [hvname for hvname in self.enabled_hypervisors
819 066f465d Guido Trotter
         if hvname != self.default_hypervisor]
820 066f465d Guido Trotter
      self.default_hypervisor = None
821 066f465d Guido Trotter
822 066f465d Guido Trotter
823 319856a9 Michael Hanselmann
  def ToDict(self):
824 319856a9 Michael Hanselmann
    """Custom function for cluster.
825 319856a9 Michael Hanselmann

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

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

844 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
845 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
846 5bf7b5cf Iustin Pop
    @rtype: dict
847 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
848 5bf7b5cf Iustin Pop
        the cluster defaults
849 5bf7b5cf Iustin Pop

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

857 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
858 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
859 5bf7b5cf Iustin Pop
    @rtype: dict
860 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
861 5bf7b5cf Iustin Pop
        the cluster defaults
862 5bf7b5cf Iustin Pop

863 5bf7b5cf Iustin Pop
    """
864 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
865 4ef7f423 Guido Trotter
                          instance.beparams)
866 5bf7b5cf Iustin Pop
867 5c947f38 Iustin Pop
868 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
869 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
870 96acbc09 Michael Hanselmann
  __slots__ = [
871 96acbc09 Michael Hanselmann
    "dev_path",
872 96acbc09 Michael Hanselmann
    "major",
873 96acbc09 Michael Hanselmann
    "minor",
874 96acbc09 Michael Hanselmann
    "sync_percent",
875 96acbc09 Michael Hanselmann
    "estimated_time",
876 96acbc09 Michael Hanselmann
    "is_degraded",
877 f208978a Michael Hanselmann
    "ldisk_status",
878 96acbc09 Michael Hanselmann
    ]
879 96acbc09 Michael Hanselmann
880 96acbc09 Michael Hanselmann
881 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
882 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
883 a8083063 Iustin Pop

884 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
885 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
886 a8083063 Iustin Pop
  buffer.
887 a8083063 Iustin Pop

888 a8083063 Iustin Pop
  """
889 a8083063 Iustin Pop
  def Dumps(self):
890 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
891 a8083063 Iustin Pop
    buf = StringIO()
892 a8083063 Iustin Pop
    self.write(buf)
893 a8083063 Iustin Pop
    return buf.getvalue()
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
  @staticmethod
896 a8083063 Iustin Pop
  def Loads(data):
897 a8083063 Iustin Pop
    """Load data from a string."""
898 a8083063 Iustin Pop
    buf = StringIO(data)
899 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
900 a8083063 Iustin Pop
    cfp.readfp(buf)
901 a8083063 Iustin Pop
    return cfp