Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ debac808

History | View | Annotate | Download (28.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 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 b5e5632e Iustin Pop
  VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
214 2057f6c7 Iustin Pop
215 b5e5632e Iustin Pop
  @classmethod
216 b5e5632e Iustin Pop
  def ValidateTag(cls, tag):
217 5c947f38 Iustin Pop
    """Check if a tag is valid.
218 5c947f38 Iustin Pop

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

222 5c947f38 Iustin Pop
    """
223 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
224 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
225 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
226 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
227 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
228 5c947f38 Iustin Pop
    if not tag:
229 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
230 b5e5632e Iustin Pop
    if not cls.VALID_TAG_RE.match(tag):
231 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
232 5c947f38 Iustin Pop
233 5c947f38 Iustin Pop
  def GetTags(self):
234 5c947f38 Iustin Pop
    """Return the tags list.
235 5c947f38 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

747 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
748 ff9c047c Iustin Pop
    python types.
749 ff9c047c Iustin Pop

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

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

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

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

875 319856a9 Michael Hanselmann
    """
876 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
877 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
878 319856a9 Michael Hanselmann
    return mydict
879 319856a9 Michael Hanselmann
880 319856a9 Michael Hanselmann
  @classmethod
881 319856a9 Michael Hanselmann
  def FromDict(cls, val):
882 319856a9 Michael Hanselmann
    """Custom function for cluster.
883 319856a9 Michael Hanselmann

884 319856a9 Michael Hanselmann
    """
885 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
886 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
887 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
888 319856a9 Michael Hanselmann
    return obj
889 319856a9 Michael Hanselmann
890 5bf7b5cf Iustin Pop
  def FillHV(self, instance):
891 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
892 5bf7b5cf Iustin Pop

893 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
894 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
895 5bf7b5cf Iustin Pop
    @rtype: dict
896 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
897 5bf7b5cf Iustin Pop
        the cluster defaults
898 5bf7b5cf Iustin Pop

899 5bf7b5cf Iustin Pop
    """
900 abe609b2 Guido Trotter
    return FillDict(self.hvparams.get(instance.hypervisor, {}),
901 5bf7b5cf Iustin Pop
                         instance.hvparams)
902 5bf7b5cf Iustin Pop
903 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
904 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
905 5bf7b5cf Iustin Pop

906 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
907 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
908 5bf7b5cf Iustin Pop
    @rtype: dict
909 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
910 5bf7b5cf Iustin Pop
        the cluster defaults
911 5bf7b5cf Iustin Pop

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

933 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
934 18d750b9 Guido Trotter
  @ivar type: confd query type
935 18d750b9 Guido Trotter
  @ivar query: query request
936 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
937 18d750b9 Guido Trotter

938 18d750b9 Guido Trotter
  """
939 18d750b9 Guido Trotter
  __slots__ = [
940 18d750b9 Guido Trotter
    "protocol",
941 18d750b9 Guido Trotter
    "type",
942 18d750b9 Guido Trotter
    "query",
943 18d750b9 Guido Trotter
    "rsalt",
944 18d750b9 Guido Trotter
    ]
945 18d750b9 Guido Trotter
946 18d750b9 Guido Trotter
947 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
948 18d750b9 Guido Trotter
  """Object holding a confd reply.
949 18d750b9 Guido Trotter

950 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
951 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
952 18d750b9 Guido Trotter
  @ivar answer: confd query reply
953 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
954 18d750b9 Guido Trotter

955 18d750b9 Guido Trotter
  """
956 18d750b9 Guido Trotter
  __slots__ = [
957 18d750b9 Guido Trotter
    "protocol",
958 18d750b9 Guido Trotter
    "status",
959 18d750b9 Guido Trotter
    "answer",
960 18d750b9 Guido Trotter
    "serial",
961 18d750b9 Guido Trotter
    ]
962 18d750b9 Guido Trotter
963 18d750b9 Guido Trotter
964 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
965 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
966 a8083063 Iustin Pop

967 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
968 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
969 a8083063 Iustin Pop
  buffer.
970 a8083063 Iustin Pop

971 a8083063 Iustin Pop
  """
972 a8083063 Iustin Pop
  def Dumps(self):
973 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
974 a8083063 Iustin Pop
    buf = StringIO()
975 a8083063 Iustin Pop
    self.write(buf)
976 a8083063 Iustin Pop
    return buf.getvalue()
977 a8083063 Iustin Pop
978 a8083063 Iustin Pop
  @staticmethod
979 a8083063 Iustin Pop
  def Loads(data):
980 a8083063 Iustin Pop
    """Load data from a string."""
981 a8083063 Iustin Pop
    buf = StringIO(data)
982 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
983 a8083063 Iustin Pop
    cfp.readfp(buf)
984 a8083063 Iustin Pop
    return cfp