Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ cf7b0cc4

History | View | Annotate | Download (28.4 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 d693c864 Iustin Pop
_TIMESTAMPS = ["ctime", "mtime"]
43 e1dcc53a Iustin Pop
_UUID = ["uuid"]
44 96acbc09 Michael Hanselmann
45 abe609b2 Guido Trotter
def FillDict(defaults_dict, custom_dict):
46 29921401 Iustin Pop
  """Basic function to apply settings on top a default dict.
47 abe609b2 Guido Trotter

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

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

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

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

81 a8083063 Iustin Pop
  It has the following properties:
82 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

173 ff9c047c Iustin Pop
    """
174 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
175 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
176 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
177 ff9c047c Iustin Pop
    if c_type is dict:
178 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
179 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
180 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
181 ff9c047c Iustin Pop
    else:
182 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
183 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
184 ff9c047c Iustin Pop
    return ret
185 ff9c047c Iustin Pop
186 e8d563f3 Iustin Pop
  def Copy(self):
187 e8d563f3 Iustin Pop
    """Makes a deep copy of the current object and its children.
188 e8d563f3 Iustin Pop

189 e8d563f3 Iustin Pop
    """
190 e8d563f3 Iustin Pop
    dict_form = self.ToDict()
191 e8d563f3 Iustin Pop
    clone_obj = self.__class__.FromDict(dict_form)
192 e8d563f3 Iustin Pop
    return clone_obj
193 e8d563f3 Iustin Pop
194 ff9c047c Iustin Pop
  def __repr__(self):
195 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
196 ff9c047c Iustin Pop
    return repr(self.ToDict())
197 ff9c047c Iustin Pop
198 560428be Guido Trotter
  def UpgradeConfig(self):
199 560428be Guido Trotter
    """Fill defaults for missing configuration values.
200 560428be Guido Trotter

201 90d726a8 Iustin Pop
    This method will be called at configuration load time, and its
202 90d726a8 Iustin Pop
    implementation will be object dependent.
203 560428be Guido Trotter

204 560428be Guido Trotter
    """
205 560428be Guido Trotter
    pass
206 560428be Guido Trotter
207 a8083063 Iustin Pop
208 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
209 5c947f38 Iustin Pop
  """An generic class supporting tags.
210 5c947f38 Iustin Pop

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

218 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
219 5c947f38 Iustin Pop
    function has no return value.
220 5c947f38 Iustin Pop

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

235 5c947f38 Iustin Pop
    """
236 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
237 5c947f38 Iustin Pop
    if tags is None:
238 5c947f38 Iustin Pop
      tags = self.tags = set()
239 5c947f38 Iustin Pop
    return tags
240 5c947f38 Iustin Pop
241 5c947f38 Iustin Pop
  def AddTag(self, tag):
242 5c947f38 Iustin Pop
    """Add a new tag.
243 5c947f38 Iustin Pop

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

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

265 ff9c047c Iustin Pop
    This replaces the tags set with a list.
266 ff9c047c Iustin Pop

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

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

294 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
295 ff9c047c Iustin Pop
    with standard python types.
296 ff9c047c Iustin Pop

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

309 ff9c047c Iustin Pop
    """
310 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
311 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
312 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
313 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
314 ff9c047c Iustin Pop
    return obj
315 ff9c047c Iustin Pop
316 90d726a8 Iustin Pop
  def UpgradeConfig(self):
317 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
318 90d726a8 Iustin Pop

319 90d726a8 Iustin Pop
    """
320 90d726a8 Iustin Pop
    self.cluster.UpgradeConfig()
321 90d726a8 Iustin Pop
    for node in self.nodes.values():
322 90d726a8 Iustin Pop
      node.UpgradeConfig()
323 90d726a8 Iustin Pop
    for instance in self.instances.values():
324 90d726a8 Iustin Pop
      instance.UpgradeConfig()
325 90d726a8 Iustin Pop
326 a8083063 Iustin Pop
327 a8083063 Iustin Pop
class NIC(ConfigObject):
328 a8083063 Iustin Pop
  """Config object representing a network card."""
329 13f1af63 Guido Trotter
  __slots__ = ["mac", "ip", "bridge", "nicparams"]
330 a8083063 Iustin Pop
331 255e19d4 Guido Trotter
  @classmethod
332 255e19d4 Guido Trotter
  def CheckParameterSyntax(cls, nicparams):
333 255e19d4 Guido Trotter
    """Check the given parameters for validity.
334 255e19d4 Guido Trotter

335 255e19d4 Guido Trotter
    @type nicparams:  dict
336 255e19d4 Guido Trotter
    @param nicparams: dictionary with parameter names/value
337 255e19d4 Guido Trotter
    @raise errors.ConfigurationError: when a parameter is not valid
338 255e19d4 Guido Trotter

339 255e19d4 Guido Trotter
    """
340 255e19d4 Guido Trotter
    if nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES:
341 255e19d4 Guido Trotter
      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
342 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
343 255e19d4 Guido Trotter
344 255e19d4 Guido Trotter
    if (nicparams[constants.NIC_MODE] is constants.NIC_MODE_BRIDGED and
345 255e19d4 Guido Trotter
        not nicparams[constants.NIC_LINK]):
346 255e19d4 Guido Trotter
      err = "Missing bridged nic link"
347 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
348 255e19d4 Guido Trotter
349 13f1af63 Guido Trotter
  def UpgradeConfig(self):
350 13f1af63 Guido Trotter
    """Fill defaults for missing configuration values.
351 13f1af63 Guido Trotter

352 13f1af63 Guido Trotter
    """
353 13f1af63 Guido Trotter
    if self.nicparams is None:
354 13f1af63 Guido Trotter
      self.nicparams = {}
355 13f1af63 Guido Trotter
      if self.bridge is not None:
356 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_MODE] = constants.NIC_MODE_BRIDGED
357 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_LINK] = self.bridge
358 9b31ca85 Guido Trotter
    # bridge is no longer used it 2.1. The slot is left there to support
359 9b31ca85 Guido Trotter
    # upgrading, but will be removed in 2.2
360 9b31ca85 Guido Trotter
    if self.bridge is not None:
361 9b31ca85 Guido Trotter
      self.bridge = None
362 13f1af63 Guido Trotter
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
class Disk(ConfigObject):
365 a8083063 Iustin Pop
  """Config object representing a block device."""
366 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
367 08db7c5c Iustin Pop
               "children", "iv_name", "size", "mode"]
368 a8083063 Iustin Pop
369 a8083063 Iustin Pop
  def CreateOnSecondary(self):
370 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
371 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
372 a8083063 Iustin Pop
373 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
374 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
375 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
376 a8083063 Iustin Pop
377 a8083063 Iustin Pop
  def OpenOnSecondary(self):
378 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
379 fe96220b Iustin Pop
    return self.dev_type in (constants.LD_LV,)
380 a8083063 Iustin Pop
381 222f2dd5 Iustin Pop
  def StaticDevPath(self):
382 222f2dd5 Iustin Pop
    """Return the device path if this device type has a static one.
383 222f2dd5 Iustin Pop

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

388 222f2dd5 Iustin Pop
    """
389 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
390 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
391 222f2dd5 Iustin Pop
    return None
392 222f2dd5 Iustin Pop
393 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
394 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
395 fc1dc9d7 Iustin Pop

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

400 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
401 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
402 fc1dc9d7 Iustin Pop
    -1.
403 fc1dc9d7 Iustin Pop

404 fc1dc9d7 Iustin Pop
    """
405 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
406 fc1dc9d7 Iustin Pop
      return 0
407 fc1dc9d7 Iustin Pop
    return -1
408 fc1dc9d7 Iustin Pop
409 a8083063 Iustin Pop
  def GetNodes(self, node):
410 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
411 a8083063 Iustin Pop

412 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
413 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
414 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
415 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
416 a8083063 Iustin Pop

417 a8083063 Iustin Pop
    """
418 00fb8246 Michael Hanselmann
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
419 a8083063 Iustin Pop
      result = [node]
420 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
421 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
422 a8083063 Iustin Pop
      if node not in result:
423 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
424 a8083063 Iustin Pop
    else:
425 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
426 a8083063 Iustin Pop
    return result
427 a8083063 Iustin Pop
428 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
429 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
430 a8083063 Iustin Pop

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

437 a8083063 Iustin Pop
    """
438 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
439 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
440 a8083063 Iustin Pop
    if not self.children:
441 a8083063 Iustin Pop
      # leaf device
442 a8083063 Iustin Pop
      return result
443 a8083063 Iustin Pop
    for node in my_nodes:
444 a8083063 Iustin Pop
      for child in self.children:
445 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
446 a8083063 Iustin Pop
        if len(child_result) == 1:
447 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
448 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
449 a8083063 Iustin Pop
          # own entry for this node describes it completely
450 a8083063 Iustin Pop
          continue
451 a8083063 Iustin Pop
        else:
452 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
453 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
454 a8083063 Iustin Pop
          # one of its descendants
455 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
456 a8083063 Iustin Pop
            if subnode not in my_nodes:
457 a8083063 Iustin Pop
              result.append((subnode, subdisk))
458 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
459 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
460 a8083063 Iustin Pop
            # be different)
461 a8083063 Iustin Pop
    return result
462 a8083063 Iustin Pop
463 acec9d51 Iustin Pop
  def RecordGrow(self, amount):
464 acec9d51 Iustin Pop
    """Update the size of this disk after growth.
465 acec9d51 Iustin Pop

466 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
467 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
468 acec9d51 Iustin Pop
    actual algorithms from bdev.
469 acec9d51 Iustin Pop

470 acec9d51 Iustin Pop
    """
471 acec9d51 Iustin Pop
    if self.dev_type == constants.LD_LV:
472 acec9d51 Iustin Pop
      self.size += amount
473 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
474 acec9d51 Iustin Pop
      if self.children:
475 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
476 acec9d51 Iustin Pop
      self.size += amount
477 acec9d51 Iustin Pop
    else:
478 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
479 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
480 acec9d51 Iustin Pop
481 a805ec18 Iustin Pop
  def UnsetSize(self):
482 a805ec18 Iustin Pop
    """Sets recursively the size to zero for the disk and its children.
483 a805ec18 Iustin Pop

484 a805ec18 Iustin Pop
    """
485 a805ec18 Iustin Pop
    if self.children:
486 a805ec18 Iustin Pop
      for child in self.children:
487 a805ec18 Iustin Pop
        child.UnsetSize()
488 a805ec18 Iustin Pop
    self.size = 0
489 a805ec18 Iustin Pop
490 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
491 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
492 0402302c Iustin Pop

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

495 0402302c Iustin Pop
    The routine descends down and updates its children also, because
496 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
497 0402302c Iustin Pop
    node.
498 0402302c Iustin Pop

499 0402302c Iustin Pop
    Arguments:
500 0402302c Iustin Pop
      - target_node: the node we wish to configure for
501 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
502 0402302c Iustin Pop

503 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
504 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
505 0402302c Iustin Pop
    in the disk tree.
506 0402302c Iustin Pop

507 0402302c Iustin Pop
    """
508 0402302c Iustin Pop
    if self.children:
509 0402302c Iustin Pop
      for child in self.children:
510 0402302c Iustin Pop
        child.SetPhysicalID(target_node, nodes_ip)
511 0402302c Iustin Pop
512 0402302c Iustin Pop
    if self.logical_id is None and self.physical_id is not None:
513 0402302c Iustin Pop
      return
514 0402302c Iustin Pop
    if self.dev_type in constants.LDS_DRBD:
515 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = self.logical_id
516 0402302c Iustin Pop
      if target_node not in (pnode, snode):
517 0402302c Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
518 0402302c Iustin Pop
                                        target_node)
519 0402302c Iustin Pop
      pnode_ip = nodes_ip.get(pnode, None)
520 0402302c Iustin Pop
      snode_ip = nodes_ip.get(snode, None)
521 0402302c Iustin Pop
      if pnode_ip is None or snode_ip is None:
522 0402302c Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
523 0402302c Iustin Pop
                                        " for %s" % str(self))
524 ffa1c0dc Iustin Pop
      p_data = (pnode_ip, port)
525 ffa1c0dc Iustin Pop
      s_data = (snode_ip, port)
526 0402302c Iustin Pop
      if pnode == target_node:
527 f9518d38 Iustin Pop
        self.physical_id = p_data + s_data + (pminor, secret)
528 0402302c Iustin Pop
      else: # it must be secondary, we tested above
529 f9518d38 Iustin Pop
        self.physical_id = s_data + p_data + (sminor, secret)
530 0402302c Iustin Pop
    else:
531 0402302c Iustin Pop
      self.physical_id = self.logical_id
532 0402302c Iustin Pop
    return
533 0402302c Iustin Pop
534 ff9c047c Iustin Pop
  def ToDict(self):
535 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
536 ff9c047c Iustin Pop

537 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
538 ff9c047c Iustin Pop
    standard python types.
539 ff9c047c Iustin Pop

540 ff9c047c Iustin Pop
    """
541 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
542 ff9c047c Iustin Pop
543 ff9c047c Iustin Pop
    for attr in ("children",):
544 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
545 ff9c047c Iustin Pop
      if alist:
546 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
547 ff9c047c Iustin Pop
    return bo
548 ff9c047c Iustin Pop
549 ff9c047c Iustin Pop
  @classmethod
550 ff9c047c Iustin Pop
  def FromDict(cls, val):
551 ff9c047c Iustin Pop
    """Custom function for Disks
552 ff9c047c Iustin Pop

553 ff9c047c Iustin Pop
    """
554 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
555 ff9c047c Iustin Pop
    if obj.children:
556 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
557 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
558 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
559 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
560 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
561 f9518d38 Iustin Pop
    if obj.dev_type in constants.LDS_DRBD:
562 f9518d38 Iustin Pop
      # we need a tuple of length six here
563 f9518d38 Iustin Pop
      if len(obj.logical_id) < 6:
564 f9518d38 Iustin Pop
        obj.logical_id += (None,) * (6 - len(obj.logical_id))
565 ff9c047c Iustin Pop
    return obj
566 ff9c047c Iustin Pop
567 65a15336 Iustin Pop
  def __str__(self):
568 65a15336 Iustin Pop
    """Custom str() formatter for disks.
569 65a15336 Iustin Pop

570 65a15336 Iustin Pop
    """
571 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
572 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
573 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
574 89f28b76 Iustin Pop
      node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
575 00fb8246 Michael Hanselmann
      val = "<DRBD8("
576 073ca59e Iustin Pop
      if self.physical_id is None:
577 073ca59e Iustin Pop
        phy = "unconfigured"
578 073ca59e Iustin Pop
      else:
579 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
580 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
581 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
582 073ca59e Iustin Pop
583 89f28b76 Iustin Pop
      val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
584 89f28b76 Iustin Pop
              (node_a, minor_a, node_b, minor_b, port, phy))
585 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
586 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
587 65a15336 Iustin Pop
      else:
588 65a15336 Iustin Pop
        val += "no local storage"
589 65a15336 Iustin Pop
    else:
590 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
591 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
592 65a15336 Iustin Pop
    if self.iv_name is None:
593 65a15336 Iustin Pop
      val += ", not visible"
594 65a15336 Iustin Pop
    else:
595 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
596 fd965830 Iustin Pop
    if isinstance(self.size, int):
597 fd965830 Iustin Pop
      val += ", size=%dm)>" % self.size
598 fd965830 Iustin Pop
    else:
599 fd965830 Iustin Pop
      val += ", size='%s')>" % (self.size,)
600 65a15336 Iustin Pop
    return val
601 65a15336 Iustin Pop
602 332d0e37 Iustin Pop
  def Verify(self):
603 332d0e37 Iustin Pop
    """Checks that this disk is correctly configured.
604 332d0e37 Iustin Pop

605 332d0e37 Iustin Pop
    """
606 7c4d6c7b Michael Hanselmann
    all_errors = []
607 332d0e37 Iustin Pop
    if self.mode not in constants.DISK_ACCESS_SET:
608 7c4d6c7b Michael Hanselmann
      all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
609 7c4d6c7b Michael Hanselmann
    return all_errors
610 332d0e37 Iustin Pop
611 90d726a8 Iustin Pop
  def UpgradeConfig(self):
612 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
613 90d726a8 Iustin Pop

614 90d726a8 Iustin Pop
    """
615 90d726a8 Iustin Pop
    if self.children:
616 90d726a8 Iustin Pop
      for child in self.children:
617 90d726a8 Iustin Pop
        child.UpgradeConfig()
618 90d726a8 Iustin Pop
    # add here config upgrade for this disk
619 90d726a8 Iustin Pop
620 a8083063 Iustin Pop
621 ec29fe40 Iustin Pop
class Instance(TaggableObject):
622 a8083063 Iustin Pop
  """Config object representing an instance."""
623 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
624 a8083063 Iustin Pop
    "name",
625 a8083063 Iustin Pop
    "primary_node",
626 a8083063 Iustin Pop
    "os",
627 e69d05fd Iustin Pop
    "hypervisor",
628 5bf7b5cf Iustin Pop
    "hvparams",
629 5bf7b5cf Iustin Pop
    "beparams",
630 0d68c45d Iustin Pop
    "admin_up",
631 a8083063 Iustin Pop
    "nics",
632 a8083063 Iustin Pop
    "disks",
633 a8083063 Iustin Pop
    "disk_template",
634 58acb49d Alexander Schreiber
    "network_port",
635 be1fa613 Iustin Pop
    "serial_no",
636 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
637 a8083063 Iustin Pop
638 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
639 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
640 a8083063 Iustin Pop

641 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
642 cfcc5c6d Iustin Pop

643 cfcc5c6d Iustin Pop
    """
644 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
645 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
646 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
647 cfcc5c6d Iustin Pop
648 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
649 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
650 cfcc5c6d Iustin Pop
651 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
652 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
653 cfcc5c6d Iustin Pop

654 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
655 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
656 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
657 a8083063 Iustin Pop
    dynamically.
658 a8083063 Iustin Pop

659 a8083063 Iustin Pop
    """
660 cfcc5c6d Iustin Pop
    def _Helper(nodes, device):
661 cfcc5c6d Iustin Pop
      """Recursively computes nodes given a top device."""
662 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
663 cfcc5c6d Iustin Pop
        nodea, nodeb = device.logical_id[:2]
664 cfcc5c6d Iustin Pop
        nodes.add(nodea)
665 cfcc5c6d Iustin Pop
        nodes.add(nodeb)
666 a8083063 Iustin Pop
      if device.children:
667 a8083063 Iustin Pop
        for child in device.children:
668 cfcc5c6d Iustin Pop
          _Helper(nodes, child)
669 a8083063 Iustin Pop
670 cfcc5c6d Iustin Pop
    all_nodes = set()
671 99c7b2a1 Iustin Pop
    all_nodes.add(self.primary_node)
672 a8083063 Iustin Pop
    for device in self.disks:
673 cfcc5c6d Iustin Pop
      _Helper(all_nodes, device)
674 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
675 a8083063 Iustin Pop
676 cfcc5c6d Iustin Pop
  all_nodes = property(_ComputeAllNodes, None, None,
677 cfcc5c6d Iustin Pop
                       "List of all nodes of the instance")
678 a8083063 Iustin Pop
679 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
680 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
681 a8083063 Iustin Pop

682 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
683 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
684 a8083063 Iustin Pop

685 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
686 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
687 a8083063 Iustin Pop

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

691 a8083063 Iustin Pop
    """
692 a8083063 Iustin Pop
    if node == None:
693 a8083063 Iustin Pop
      node = self.primary_node
694 a8083063 Iustin Pop
695 a8083063 Iustin Pop
    if lvmap is None:
696 a8083063 Iustin Pop
      lvmap = { node : [] }
697 a8083063 Iustin Pop
      ret = lvmap
698 a8083063 Iustin Pop
    else:
699 a8083063 Iustin Pop
      if not node in lvmap:
700 a8083063 Iustin Pop
        lvmap[node] = []
701 a8083063 Iustin Pop
      ret = None
702 a8083063 Iustin Pop
703 a8083063 Iustin Pop
    if not devs:
704 a8083063 Iustin Pop
      devs = self.disks
705 a8083063 Iustin Pop
706 a8083063 Iustin Pop
    for dev in devs:
707 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
708 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
709 a8083063 Iustin Pop
710 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
711 a8083063 Iustin Pop
        if dev.children:
712 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
713 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
714 a8083063 Iustin Pop
715 a8083063 Iustin Pop
      elif dev.children:
716 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
717 a8083063 Iustin Pop
718 a8083063 Iustin Pop
    return ret
719 a8083063 Iustin Pop
720 ad24e046 Iustin Pop
  def FindDisk(self, idx):
721 ad24e046 Iustin Pop
    """Find a disk given having a specified index.
722 644eeef9 Iustin Pop

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

725 ad24e046 Iustin Pop
    @type idx: int
726 ad24e046 Iustin Pop
    @param idx: the disk index
727 ad24e046 Iustin Pop
    @rtype: L{Disk}
728 ad24e046 Iustin Pop
    @return: the corresponding disk
729 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
730 644eeef9 Iustin Pop

731 ad24e046 Iustin Pop
    """
732 ad24e046 Iustin Pop
    try:
733 ad24e046 Iustin Pop
      idx = int(idx)
734 ad24e046 Iustin Pop
      return self.disks[idx]
735 ad24e046 Iustin Pop
    except ValueError, err:
736 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err))
737 ad24e046 Iustin Pop
    except IndexError:
738 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
739 ad24e046 Iustin Pop
                                 " 0 to %d" % (idx, len(self.disks)))
740 644eeef9 Iustin Pop
741 ff9c047c Iustin Pop
  def ToDict(self):
742 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
743 ff9c047c Iustin Pop

744 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
745 ff9c047c Iustin Pop
    python types.
746 ff9c047c Iustin Pop

747 ff9c047c Iustin Pop
    """
748 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
749 ff9c047c Iustin Pop
750 ff9c047c Iustin Pop
    for attr in "nics", "disks":
751 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
752 ff9c047c Iustin Pop
      if alist:
753 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
754 ff9c047c Iustin Pop
      else:
755 ff9c047c Iustin Pop
        nlist = []
756 ff9c047c Iustin Pop
      bo[attr] = nlist
757 ff9c047c Iustin Pop
    return bo
758 ff9c047c Iustin Pop
759 ff9c047c Iustin Pop
  @classmethod
760 ff9c047c Iustin Pop
  def FromDict(cls, val):
761 ff9c047c Iustin Pop
    """Custom function for instances.
762 ff9c047c Iustin Pop

763 ff9c047c Iustin Pop
    """
764 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
765 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
766 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
767 ff9c047c Iustin Pop
    return obj
768 ff9c047c Iustin Pop
769 90d726a8 Iustin Pop
  def UpgradeConfig(self):
770 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
771 90d726a8 Iustin Pop

772 90d726a8 Iustin Pop
    """
773 90d726a8 Iustin Pop
    for nic in self.nics:
774 90d726a8 Iustin Pop
      nic.UpgradeConfig()
775 90d726a8 Iustin Pop
    for disk in self.disks:
776 90d726a8 Iustin Pop
      disk.UpgradeConfig()
777 90d726a8 Iustin Pop
778 a8083063 Iustin Pop
779 a8083063 Iustin Pop
class OS(ConfigObject):
780 a8083063 Iustin Pop
  """Config object representing an operating system."""
781 a8083063 Iustin Pop
  __slots__ = [
782 a8083063 Iustin Pop
    "name",
783 a8083063 Iustin Pop
    "path",
784 082a7f91 Guido Trotter
    "api_versions",
785 a8083063 Iustin Pop
    "create_script",
786 a8083063 Iustin Pop
    "export_script",
787 386b57af Iustin Pop
    "import_script",
788 386b57af Iustin Pop
    "rename_script",
789 a8083063 Iustin Pop
    ]
790 a8083063 Iustin Pop
791 7c0d6283 Michael Hanselmann
792 ec29fe40 Iustin Pop
class Node(TaggableObject):
793 a8083063 Iustin Pop
  """Config object representing a node."""
794 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
795 ec29fe40 Iustin Pop
    "name",
796 ec29fe40 Iustin Pop
    "primary_ip",
797 ec29fe40 Iustin Pop
    "secondary_ip",
798 be1fa613 Iustin Pop
    "serial_no",
799 8b8b8b81 Iustin Pop
    "master_candidate",
800 fc0fe88c Iustin Pop
    "offline",
801 af64c0ea Iustin Pop
    "drained",
802 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
803 a8083063 Iustin Pop
804 a8083063 Iustin Pop
805 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
806 a8083063 Iustin Pop
  """Config object representing the cluster."""
807 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
808 a8083063 Iustin Pop
    "serial_no",
809 a8083063 Iustin Pop
    "rsahostkeypub",
810 a8083063 Iustin Pop
    "highest_used_port",
811 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
812 a8083063 Iustin Pop
    "mac_prefix",
813 a8083063 Iustin Pop
    "volume_group_name",
814 a8083063 Iustin Pop
    "default_bridge",
815 02691904 Alexander Schreiber
    "default_hypervisor",
816 f6bd6e98 Michael Hanselmann
    "master_node",
817 f6bd6e98 Michael Hanselmann
    "master_ip",
818 f6bd6e98 Michael Hanselmann
    "master_netdev",
819 f6bd6e98 Michael Hanselmann
    "cluster_name",
820 f6bd6e98 Michael Hanselmann
    "file_storage_dir",
821 e69d05fd Iustin Pop
    "enabled_hypervisors",
822 5bf7b5cf Iustin Pop
    "hvparams",
823 5bf7b5cf Iustin Pop
    "beparams",
824 c8fcde47 Guido Trotter
    "nicparams",
825 4b7735f9 Iustin Pop
    "candidate_pool_size",
826 b86a6bcd Guido Trotter
    "modify_etc_hosts",
827 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
828 a8083063 Iustin Pop
829 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
830 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
831 b86a6bcd Guido Trotter

832 b86a6bcd Guido Trotter
    """
833 c1b42c18 Guido Trotter
    if self.hvparams is None:
834 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
835 c1b42c18 Guido Trotter
    else:
836 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
837 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
838 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
839 c1b42c18 Guido Trotter
840 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
841 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
842 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
843 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
844 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
845 c8fcde47 Guido Trotter
    if migrate_default_bridge:
846 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
847 c8fcde47 Guido Trotter
        self.default_bridge
848 c1b42c18 Guido Trotter
849 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
850 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
851 b86a6bcd Guido Trotter
852 9b31ca85 Guido Trotter
    # default_bridge is no longer used it 2.1. The slot is left there to
853 9b31ca85 Guido Trotter
    # support auto-upgrading, but will be removed in 2.2
854 9b31ca85 Guido Trotter
    if self.default_bridge is not None:
855 9b31ca85 Guido Trotter
      self.default_bridge = None
856 9b31ca85 Guido Trotter
857 066f465d Guido Trotter
    # default_hypervisor is just the first enabled one in 2.1
858 066f465d Guido Trotter
    if self.default_hypervisor is not None:
859 016d04b3 Michael Hanselmann
      self.enabled_hypervisors = ([self.default_hypervisor] +
860 066f465d Guido Trotter
        [hvname for hvname in self.enabled_hypervisors
861 016d04b3 Michael Hanselmann
         if hvname != self.default_hypervisor])
862 066f465d Guido Trotter
      self.default_hypervisor = None
863 066f465d Guido Trotter
864 319856a9 Michael Hanselmann
  def ToDict(self):
865 319856a9 Michael Hanselmann
    """Custom function for cluster.
866 319856a9 Michael Hanselmann

867 319856a9 Michael Hanselmann
    """
868 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
869 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
870 319856a9 Michael Hanselmann
    return mydict
871 319856a9 Michael Hanselmann
872 319856a9 Michael Hanselmann
  @classmethod
873 319856a9 Michael Hanselmann
  def FromDict(cls, val):
874 319856a9 Michael Hanselmann
    """Custom function for cluster.
875 319856a9 Michael Hanselmann

876 319856a9 Michael Hanselmann
    """
877 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
878 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
879 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
880 319856a9 Michael Hanselmann
    return obj
881 319856a9 Michael Hanselmann
882 5bf7b5cf Iustin Pop
  def FillHV(self, instance):
883 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
884 5bf7b5cf Iustin Pop

885 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
886 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
887 5bf7b5cf Iustin Pop
    @rtype: dict
888 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
889 5bf7b5cf Iustin Pop
        the cluster defaults
890 5bf7b5cf Iustin Pop

891 5bf7b5cf Iustin Pop
    """
892 abe609b2 Guido Trotter
    return FillDict(self.hvparams.get(instance.hypervisor, {}),
893 5bf7b5cf Iustin Pop
                         instance.hvparams)
894 5bf7b5cf Iustin Pop
895 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
896 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
897 5bf7b5cf Iustin Pop

898 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
899 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
900 5bf7b5cf Iustin Pop
    @rtype: dict
901 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
902 5bf7b5cf Iustin Pop
        the cluster defaults
903 5bf7b5cf Iustin Pop

904 5bf7b5cf Iustin Pop
    """
905 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
906 4ef7f423 Guido Trotter
                          instance.beparams)
907 5bf7b5cf Iustin Pop
908 5c947f38 Iustin Pop
909 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
910 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
911 96acbc09 Michael Hanselmann
  __slots__ = [
912 96acbc09 Michael Hanselmann
    "dev_path",
913 96acbc09 Michael Hanselmann
    "major",
914 96acbc09 Michael Hanselmann
    "minor",
915 96acbc09 Michael Hanselmann
    "sync_percent",
916 96acbc09 Michael Hanselmann
    "estimated_time",
917 96acbc09 Michael Hanselmann
    "is_degraded",
918 f208978a Michael Hanselmann
    "ldisk_status",
919 96acbc09 Michael Hanselmann
    ]
920 96acbc09 Michael Hanselmann
921 96acbc09 Michael Hanselmann
922 18d750b9 Guido Trotter
class ConfdRequest(ConfigObject):
923 18d750b9 Guido Trotter
  """Object holding a confd request.
924 18d750b9 Guido Trotter

925 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
926 18d750b9 Guido Trotter
  @ivar type: confd query type
927 18d750b9 Guido Trotter
  @ivar query: query request
928 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
929 18d750b9 Guido Trotter

930 18d750b9 Guido Trotter
  """
931 18d750b9 Guido Trotter
  __slots__ = [
932 18d750b9 Guido Trotter
    "protocol",
933 18d750b9 Guido Trotter
    "type",
934 18d750b9 Guido Trotter
    "query",
935 18d750b9 Guido Trotter
    "rsalt",
936 18d750b9 Guido Trotter
    ]
937 18d750b9 Guido Trotter
938 18d750b9 Guido Trotter
939 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
940 18d750b9 Guido Trotter
  """Object holding a confd reply.
941 18d750b9 Guido Trotter

942 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
943 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
944 18d750b9 Guido Trotter
  @ivar answer: confd query reply
945 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
946 18d750b9 Guido Trotter

947 18d750b9 Guido Trotter
  """
948 18d750b9 Guido Trotter
  __slots__ = [
949 18d750b9 Guido Trotter
    "protocol",
950 18d750b9 Guido Trotter
    "status",
951 18d750b9 Guido Trotter
    "answer",
952 18d750b9 Guido Trotter
    "serial",
953 18d750b9 Guido Trotter
    ]
954 18d750b9 Guido Trotter
955 18d750b9 Guido Trotter
956 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
957 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
958 a8083063 Iustin Pop

959 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
960 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
961 a8083063 Iustin Pop
  buffer.
962 a8083063 Iustin Pop

963 a8083063 Iustin Pop
  """
964 a8083063 Iustin Pop
  def Dumps(self):
965 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
966 a8083063 Iustin Pop
    buf = StringIO()
967 a8083063 Iustin Pop
    self.write(buf)
968 a8083063 Iustin Pop
    return buf.getvalue()
969 a8083063 Iustin Pop
970 a8083063 Iustin Pop
  @staticmethod
971 a8083063 Iustin Pop
  def Loads(data):
972 a8083063 Iustin Pop
    """Load data from a string."""
973 a8083063 Iustin Pop
    buf = StringIO(data)
974 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
975 a8083063 Iustin Pop
    cfp.readfp(buf)
976 a8083063 Iustin Pop
    return cfp