Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ fe5b0c42

History | View | Annotate | Download (29.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 6c881c52 Iustin Pop
# pylint: disable-msg=E0203
30 6c881c52 Iustin Pop
31 6c881c52 Iustin Pop
# E0203: Access to member %r before its definition, since we use
32 6c881c52 Iustin Pop
# objects.py which doesn't explicitely initialise its members
33 6c881c52 Iustin Pop
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
import ConfigParser
36 5c947f38 Iustin Pop
import re
37 5bf7b5cf Iustin Pop
import copy
38 d5835922 Michael Hanselmann
from cStringIO import StringIO
39 a8083063 Iustin Pop
40 a8083063 Iustin Pop
from ganeti import errors
41 5c947f38 Iustin Pop
from ganeti import constants
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
45 abe609b2 Guido Trotter
           "OS", "Node", "Cluster", "FillDict"]
46 a8083063 Iustin Pop
47 d693c864 Iustin Pop
_TIMESTAMPS = ["ctime", "mtime"]
48 e1dcc53a Iustin Pop
_UUID = ["uuid"]
49 96acbc09 Michael Hanselmann
50 7736a5f2 Iustin Pop
def FillDict(defaults_dict, custom_dict, skip_keys=[]):
51 29921401 Iustin Pop
  """Basic function to apply settings on top a default dict.
52 abe609b2 Guido Trotter

53 29921401 Iustin Pop
  @type defaults_dict: dict
54 29921401 Iustin Pop
  @param defaults_dict: dictionary holding the default values
55 29921401 Iustin Pop
  @type custom_dict: dict
56 29921401 Iustin Pop
  @param custom_dict: dictionary holding customized value
57 7736a5f2 Iustin Pop
  @type skip_keys: list
58 7736a5f2 Iustin Pop
  @param skip_keys: which keys not to fill
59 29921401 Iustin Pop
  @rtype: dict
60 29921401 Iustin Pop
  @return: dict with the 'full' values
61 abe609b2 Guido Trotter

62 29921401 Iustin Pop
  """
63 29921401 Iustin Pop
  ret_dict = copy.deepcopy(defaults_dict)
64 29921401 Iustin Pop
  ret_dict.update(custom_dict)
65 7736a5f2 Iustin Pop
  for k in skip_keys:
66 7736a5f2 Iustin Pop
    try:
67 7736a5f2 Iustin Pop
      del ret_dict[k]
68 7736a5f2 Iustin Pop
    except KeyError:
69 7736a5f2 Iustin Pop
      pass
70 29921401 Iustin Pop
  return ret_dict
71 a8083063 Iustin Pop
72 6e34b628 Guido Trotter
73 6e34b628 Guido Trotter
def UpgradeGroupedParams(target, defaults):
74 6e34b628 Guido Trotter
  """Update all groups for the target parameter.
75 6e34b628 Guido Trotter

76 6e34b628 Guido Trotter
  @type target: dict of dicts
77 6e34b628 Guido Trotter
  @param target: {group: {parameter: value}}
78 6e34b628 Guido Trotter
  @type defaults: dict
79 6e34b628 Guido Trotter
  @param defaults: default parameter values
80 6e34b628 Guido Trotter

81 6e34b628 Guido Trotter
  """
82 6e34b628 Guido Trotter
  if target is None:
83 6e34b628 Guido Trotter
    target = {constants.PP_DEFAULT: defaults}
84 6e34b628 Guido Trotter
  else:
85 6e34b628 Guido Trotter
    for group in target:
86 6e34b628 Guido Trotter
      target[group] = FillDict(defaults, target[group])
87 6e34b628 Guido Trotter
  return target
88 6e34b628 Guido Trotter
89 6e34b628 Guido Trotter
90 a8083063 Iustin Pop
class ConfigObject(object):
91 a8083063 Iustin Pop
  """A generic config object.
92 a8083063 Iustin Pop

93 a8083063 Iustin Pop
  It has the following properties:
94 a8083063 Iustin Pop

95 a8083063 Iustin Pop
    - provides somewhat safe recursive unpickling and pickling for its classes
96 a8083063 Iustin Pop
    - unset attributes which are defined in slots are always returned
97 a8083063 Iustin Pop
      as None instead of raising an error
98 a8083063 Iustin Pop

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

102 a8083063 Iustin Pop
  """
103 a8083063 Iustin Pop
  __slots__ = []
104 a8083063 Iustin Pop
105 a8083063 Iustin Pop
  def __init__(self, **kwargs):
106 319856a9 Michael Hanselmann
    for k, v in kwargs.iteritems():
107 319856a9 Michael Hanselmann
      setattr(self, k, v)
108 a8083063 Iustin Pop
109 a8083063 Iustin Pop
  def __getattr__(self, name):
110 a8083063 Iustin Pop
    if name not in self.__slots__:
111 3ecf6786 Iustin Pop
      raise AttributeError("Invalid object attribute %s.%s" %
112 3ecf6786 Iustin Pop
                           (type(self).__name__, name))
113 a8083063 Iustin Pop
    return None
114 a8083063 Iustin Pop
115 a8083063 Iustin Pop
  def __setstate__(self, state):
116 a8083063 Iustin Pop
    for name in state:
117 a8083063 Iustin Pop
      if name in self.__slots__:
118 a8083063 Iustin Pop
        setattr(self, name, state[name])
119 a8083063 Iustin Pop
120 ff9c047c Iustin Pop
  def ToDict(self):
121 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
122 ff9c047c Iustin Pop

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

129 ff9c047c Iustin Pop
    """
130 4c14965f Guido Trotter
    result = {}
131 4c14965f Guido Trotter
    for name in self.__slots__:
132 4c14965f Guido Trotter
      value = getattr(self, name, None)
133 4c14965f Guido Trotter
      if value is not None:
134 4c14965f Guido Trotter
        result[name] = value
135 4c14965f Guido Trotter
    return result
136 4c14965f Guido Trotter
137 4c14965f Guido Trotter
  __getstate__ = ToDict
138 ff9c047c Iustin Pop
139 ff9c047c Iustin Pop
  @classmethod
140 ff9c047c Iustin Pop
  def FromDict(cls, val):
141 ff9c047c Iustin Pop
    """Create an object from a dictionary.
142 ff9c047c Iustin Pop

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

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

151 ff9c047c Iustin Pop
    """
152 ff9c047c Iustin Pop
    if not isinstance(val, dict):
153 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
154 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
155 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
156 319856a9 Michael Hanselmann
    obj = cls(**val_str)
157 ff9c047c Iustin Pop
    return obj
158 ff9c047c Iustin Pop
159 ff9c047c Iustin Pop
  @staticmethod
160 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
161 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
162 ff9c047c Iustin Pop

163 ff9c047c Iustin Pop
    This method converts a container with elements derived from
164 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
165 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
166 ff9c047c Iustin Pop

167 ff9c047c Iustin Pop
    """
168 ff9c047c Iustin Pop
    if isinstance(container, dict):
169 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
170 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
171 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
172 ff9c047c Iustin Pop
    else:
173 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
174 ff9c047c Iustin Pop
                      type(container))
175 ff9c047c Iustin Pop
    return ret
176 ff9c047c Iustin Pop
177 ff9c047c Iustin Pop
  @staticmethod
178 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
179 ff9c047c Iustin Pop
    """Convert a container from standard python types.
180 ff9c047c Iustin Pop

181 ff9c047c Iustin Pop
    This method converts a container with standard python types to
182 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
183 ff9c047c Iustin Pop
    keys, only the values.
184 ff9c047c Iustin Pop

185 ff9c047c Iustin Pop
    """
186 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
187 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
188 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
189 ff9c047c Iustin Pop
    if c_type is dict:
190 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
191 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
192 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
193 ff9c047c Iustin Pop
    else:
194 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
195 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
196 ff9c047c Iustin Pop
    return ret
197 ff9c047c Iustin Pop
198 e8d563f3 Iustin Pop
  def Copy(self):
199 e8d563f3 Iustin Pop
    """Makes a deep copy of the current object and its children.
200 e8d563f3 Iustin Pop

201 e8d563f3 Iustin Pop
    """
202 e8d563f3 Iustin Pop
    dict_form = self.ToDict()
203 e8d563f3 Iustin Pop
    clone_obj = self.__class__.FromDict(dict_form)
204 e8d563f3 Iustin Pop
    return clone_obj
205 e8d563f3 Iustin Pop
206 ff9c047c Iustin Pop
  def __repr__(self):
207 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
208 ff9c047c Iustin Pop
    return repr(self.ToDict())
209 ff9c047c Iustin Pop
210 560428be Guido Trotter
  def UpgradeConfig(self):
211 560428be Guido Trotter
    """Fill defaults for missing configuration values.
212 560428be Guido Trotter

213 90d726a8 Iustin Pop
    This method will be called at configuration load time, and its
214 90d726a8 Iustin Pop
    implementation will be object dependent.
215 560428be Guido Trotter

216 560428be Guido Trotter
    """
217 560428be Guido Trotter
    pass
218 560428be Guido Trotter
219 a8083063 Iustin Pop
220 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
221 5c947f38 Iustin Pop
  """An generic class supporting tags.
222 5c947f38 Iustin Pop

223 5c947f38 Iustin Pop
  """
224 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
225 b5e5632e Iustin Pop
  VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
226 2057f6c7 Iustin Pop
227 b5e5632e Iustin Pop
  @classmethod
228 b5e5632e Iustin Pop
  def ValidateTag(cls, tag):
229 5c947f38 Iustin Pop
    """Check if a tag is valid.
230 5c947f38 Iustin Pop

231 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
232 5c947f38 Iustin Pop
    function has no return value.
233 5c947f38 Iustin Pop

234 5c947f38 Iustin Pop
    """
235 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
236 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
237 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
238 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
239 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
240 5c947f38 Iustin Pop
    if not tag:
241 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
242 b5e5632e Iustin Pop
    if not cls.VALID_TAG_RE.match(tag):
243 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
244 5c947f38 Iustin Pop
245 5c947f38 Iustin Pop
  def GetTags(self):
246 5c947f38 Iustin Pop
    """Return the tags list.
247 5c947f38 Iustin Pop

248 5c947f38 Iustin Pop
    """
249 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
250 5c947f38 Iustin Pop
    if tags is None:
251 5c947f38 Iustin Pop
      tags = self.tags = set()
252 5c947f38 Iustin Pop
    return tags
253 5c947f38 Iustin Pop
254 5c947f38 Iustin Pop
  def AddTag(self, tag):
255 5c947f38 Iustin Pop
    """Add a new tag.
256 5c947f38 Iustin Pop

257 5c947f38 Iustin Pop
    """
258 5c947f38 Iustin Pop
    self.ValidateTag(tag)
259 5c947f38 Iustin Pop
    tags = self.GetTags()
260 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
261 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
262 5c947f38 Iustin Pop
    self.GetTags().add(tag)
263 5c947f38 Iustin Pop
264 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
265 5c947f38 Iustin Pop
    """Remove a tag.
266 5c947f38 Iustin Pop

267 5c947f38 Iustin Pop
    """
268 5c947f38 Iustin Pop
    self.ValidateTag(tag)
269 5c947f38 Iustin Pop
    tags = self.GetTags()
270 5c947f38 Iustin Pop
    try:
271 5c947f38 Iustin Pop
      tags.remove(tag)
272 5c947f38 Iustin Pop
    except KeyError:
273 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
274 5c947f38 Iustin Pop
275 ff9c047c Iustin Pop
  def ToDict(self):
276 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
277 ff9c047c Iustin Pop

278 ff9c047c Iustin Pop
    This replaces the tags set with a list.
279 ff9c047c Iustin Pop

280 ff9c047c Iustin Pop
    """
281 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
282 ff9c047c Iustin Pop
283 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
284 ff9c047c Iustin Pop
    if isinstance(tags, set):
285 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
286 ff9c047c Iustin Pop
    return bo
287 ff9c047c Iustin Pop
288 ff9c047c Iustin Pop
  @classmethod
289 ff9c047c Iustin Pop
  def FromDict(cls, val):
290 ff9c047c Iustin Pop
    """Custom function for instances.
291 ff9c047c Iustin Pop

292 ff9c047c Iustin Pop
    """
293 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
294 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
295 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
296 ff9c047c Iustin Pop
    return obj
297 ff9c047c Iustin Pop
298 5c947f38 Iustin Pop
299 a8083063 Iustin Pop
class ConfigData(ConfigObject):
300 a8083063 Iustin Pop
  """Top-level config object."""
301 016d04b3 Michael Hanselmann
  __slots__ = (["version", "cluster", "nodes", "instances", "serial_no"] +
302 016d04b3 Michael Hanselmann
               _TIMESTAMPS)
303 a8083063 Iustin Pop
304 ff9c047c Iustin Pop
  def ToDict(self):
305 ff9c047c Iustin Pop
    """Custom function for top-level config data.
306 ff9c047c Iustin Pop

307 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
308 ff9c047c Iustin Pop
    with standard python types.
309 ff9c047c Iustin Pop

310 ff9c047c Iustin Pop
    """
311 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
312 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
313 ff9c047c Iustin Pop
    for key in "nodes", "instances":
314 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
315 ff9c047c Iustin Pop
316 ff9c047c Iustin Pop
    return mydict
317 ff9c047c Iustin Pop
318 ff9c047c Iustin Pop
  @classmethod
319 ff9c047c Iustin Pop
  def FromDict(cls, val):
320 ff9c047c Iustin Pop
    """Custom function for top-level config data
321 ff9c047c Iustin Pop

322 ff9c047c Iustin Pop
    """
323 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
324 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
325 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
326 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
327 ff9c047c Iustin Pop
    return obj
328 ff9c047c Iustin Pop
329 90d726a8 Iustin Pop
  def UpgradeConfig(self):
330 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
331 90d726a8 Iustin Pop

332 90d726a8 Iustin Pop
    """
333 90d726a8 Iustin Pop
    self.cluster.UpgradeConfig()
334 90d726a8 Iustin Pop
    for node in self.nodes.values():
335 90d726a8 Iustin Pop
      node.UpgradeConfig()
336 90d726a8 Iustin Pop
    for instance in self.instances.values():
337 90d726a8 Iustin Pop
      instance.UpgradeConfig()
338 90d726a8 Iustin Pop
339 a8083063 Iustin Pop
340 a8083063 Iustin Pop
class NIC(ConfigObject):
341 a8083063 Iustin Pop
  """Config object representing a network card."""
342 13f1af63 Guido Trotter
  __slots__ = ["mac", "ip", "bridge", "nicparams"]
343 a8083063 Iustin Pop
344 255e19d4 Guido Trotter
  @classmethod
345 255e19d4 Guido Trotter
  def CheckParameterSyntax(cls, nicparams):
346 255e19d4 Guido Trotter
    """Check the given parameters for validity.
347 255e19d4 Guido Trotter

348 255e19d4 Guido Trotter
    @type nicparams:  dict
349 255e19d4 Guido Trotter
    @param nicparams: dictionary with parameter names/value
350 255e19d4 Guido Trotter
    @raise errors.ConfigurationError: when a parameter is not valid
351 255e19d4 Guido Trotter

352 255e19d4 Guido Trotter
    """
353 255e19d4 Guido Trotter
    if nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES:
354 255e19d4 Guido Trotter
      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
355 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
356 255e19d4 Guido Trotter
357 255e19d4 Guido Trotter
    if (nicparams[constants.NIC_MODE] is constants.NIC_MODE_BRIDGED and
358 255e19d4 Guido Trotter
        not nicparams[constants.NIC_LINK]):
359 255e19d4 Guido Trotter
      err = "Missing bridged nic link"
360 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
361 255e19d4 Guido Trotter
362 13f1af63 Guido Trotter
  def UpgradeConfig(self):
363 13f1af63 Guido Trotter
    """Fill defaults for missing configuration values.
364 13f1af63 Guido Trotter

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

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

401 222f2dd5 Iustin Pop
    """
402 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
403 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
404 222f2dd5 Iustin Pop
    return None
405 222f2dd5 Iustin Pop
406 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
407 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
408 fc1dc9d7 Iustin Pop

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

413 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
414 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
415 fc1dc9d7 Iustin Pop
    -1.
416 fc1dc9d7 Iustin Pop

417 fc1dc9d7 Iustin Pop
    """
418 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
419 fc1dc9d7 Iustin Pop
      return 0
420 fc1dc9d7 Iustin Pop
    return -1
421 fc1dc9d7 Iustin Pop
422 a8083063 Iustin Pop
  def GetNodes(self, node):
423 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
424 a8083063 Iustin Pop

425 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
426 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
427 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
428 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    """
431 00fb8246 Michael Hanselmann
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
432 a8083063 Iustin Pop
      result = [node]
433 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
434 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
435 a8083063 Iustin Pop
      if node not in result:
436 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
437 a8083063 Iustin Pop
    else:
438 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
439 a8083063 Iustin Pop
    return result
440 a8083063 Iustin Pop
441 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
442 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
443 a8083063 Iustin Pop

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

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

479 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
480 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
481 acec9d51 Iustin Pop
    actual algorithms from bdev.
482 acec9d51 Iustin Pop

483 acec9d51 Iustin Pop
    """
484 acec9d51 Iustin Pop
    if self.dev_type == constants.LD_LV:
485 acec9d51 Iustin Pop
      self.size += amount
486 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
487 acec9d51 Iustin Pop
      if self.children:
488 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
489 acec9d51 Iustin Pop
      self.size += amount
490 acec9d51 Iustin Pop
    else:
491 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
492 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
493 acec9d51 Iustin Pop
494 a805ec18 Iustin Pop
  def UnsetSize(self):
495 a805ec18 Iustin Pop
    """Sets recursively the size to zero for the disk and its children.
496 a805ec18 Iustin Pop

497 a805ec18 Iustin Pop
    """
498 a805ec18 Iustin Pop
    if self.children:
499 a805ec18 Iustin Pop
      for child in self.children:
500 a805ec18 Iustin Pop
        child.UnsetSize()
501 a805ec18 Iustin Pop
    self.size = 0
502 a805ec18 Iustin Pop
503 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
504 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
505 0402302c Iustin Pop

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

508 0402302c Iustin Pop
    The routine descends down and updates its children also, because
509 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
510 0402302c Iustin Pop
    node.
511 0402302c Iustin Pop

512 0402302c Iustin Pop
    Arguments:
513 0402302c Iustin Pop
      - target_node: the node we wish to configure for
514 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
515 0402302c Iustin Pop

516 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
517 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
518 0402302c Iustin Pop
    in the disk tree.
519 0402302c Iustin Pop

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

550 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
551 ff9c047c Iustin Pop
    standard python types.
552 ff9c047c Iustin Pop

553 ff9c047c Iustin Pop
    """
554 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
555 ff9c047c Iustin Pop
556 ff9c047c Iustin Pop
    for attr in ("children",):
557 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
558 ff9c047c Iustin Pop
      if alist:
559 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
560 ff9c047c Iustin Pop
    return bo
561 ff9c047c Iustin Pop
562 ff9c047c Iustin Pop
  @classmethod
563 ff9c047c Iustin Pop
  def FromDict(cls, val):
564 ff9c047c Iustin Pop
    """Custom function for Disks
565 ff9c047c Iustin Pop

566 ff9c047c Iustin Pop
    """
567 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
568 ff9c047c Iustin Pop
    if obj.children:
569 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
570 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
571 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
572 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
573 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
574 f9518d38 Iustin Pop
    if obj.dev_type in constants.LDS_DRBD:
575 f9518d38 Iustin Pop
      # we need a tuple of length six here
576 f9518d38 Iustin Pop
      if len(obj.logical_id) < 6:
577 f9518d38 Iustin Pop
        obj.logical_id += (None,) * (6 - len(obj.logical_id))
578 ff9c047c Iustin Pop
    return obj
579 ff9c047c Iustin Pop
580 65a15336 Iustin Pop
  def __str__(self):
581 65a15336 Iustin Pop
    """Custom str() formatter for disks.
582 65a15336 Iustin Pop

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

618 332d0e37 Iustin Pop
    """
619 7c4d6c7b Michael Hanselmann
    all_errors = []
620 332d0e37 Iustin Pop
    if self.mode not in constants.DISK_ACCESS_SET:
621 7c4d6c7b Michael Hanselmann
      all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
622 7c4d6c7b Michael Hanselmann
    return all_errors
623 332d0e37 Iustin Pop
624 90d726a8 Iustin Pop
  def UpgradeConfig(self):
625 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
626 90d726a8 Iustin Pop

627 90d726a8 Iustin Pop
    """
628 90d726a8 Iustin Pop
    if self.children:
629 90d726a8 Iustin Pop
      for child in self.children:
630 90d726a8 Iustin Pop
        child.UpgradeConfig()
631 90d726a8 Iustin Pop
    # add here config upgrade for this disk
632 90d726a8 Iustin Pop
633 a8083063 Iustin Pop
634 ec29fe40 Iustin Pop
class Instance(TaggableObject):
635 a8083063 Iustin Pop
  """Config object representing an instance."""
636 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
637 a8083063 Iustin Pop
    "name",
638 a8083063 Iustin Pop
    "primary_node",
639 a8083063 Iustin Pop
    "os",
640 e69d05fd Iustin Pop
    "hypervisor",
641 5bf7b5cf Iustin Pop
    "hvparams",
642 5bf7b5cf Iustin Pop
    "beparams",
643 0d68c45d Iustin Pop
    "admin_up",
644 a8083063 Iustin Pop
    "nics",
645 a8083063 Iustin Pop
    "disks",
646 a8083063 Iustin Pop
    "disk_template",
647 58acb49d Alexander Schreiber
    "network_port",
648 be1fa613 Iustin Pop
    "serial_no",
649 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
650 a8083063 Iustin Pop
651 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
652 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
653 a8083063 Iustin Pop

654 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
655 cfcc5c6d Iustin Pop

656 cfcc5c6d Iustin Pop
    """
657 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
658 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
659 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
660 cfcc5c6d Iustin Pop
661 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
662 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
663 cfcc5c6d Iustin Pop
664 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
665 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
666 cfcc5c6d Iustin Pop

667 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
668 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
669 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
670 a8083063 Iustin Pop
    dynamically.
671 a8083063 Iustin Pop

672 a8083063 Iustin Pop
    """
673 cfcc5c6d Iustin Pop
    def _Helper(nodes, device):
674 cfcc5c6d Iustin Pop
      """Recursively computes nodes given a top device."""
675 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
676 cfcc5c6d Iustin Pop
        nodea, nodeb = device.logical_id[:2]
677 cfcc5c6d Iustin Pop
        nodes.add(nodea)
678 cfcc5c6d Iustin Pop
        nodes.add(nodeb)
679 a8083063 Iustin Pop
      if device.children:
680 a8083063 Iustin Pop
        for child in device.children:
681 cfcc5c6d Iustin Pop
          _Helper(nodes, child)
682 a8083063 Iustin Pop
683 cfcc5c6d Iustin Pop
    all_nodes = set()
684 99c7b2a1 Iustin Pop
    all_nodes.add(self.primary_node)
685 a8083063 Iustin Pop
    for device in self.disks:
686 cfcc5c6d Iustin Pop
      _Helper(all_nodes, device)
687 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
688 a8083063 Iustin Pop
689 cfcc5c6d Iustin Pop
  all_nodes = property(_ComputeAllNodes, None, None,
690 cfcc5c6d Iustin Pop
                       "List of all nodes of the instance")
691 a8083063 Iustin Pop
692 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
693 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
694 a8083063 Iustin Pop

695 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
696 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
697 a8083063 Iustin Pop

698 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
699 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
700 a8083063 Iustin Pop

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

704 a8083063 Iustin Pop
    """
705 a8083063 Iustin Pop
    if node == None:
706 a8083063 Iustin Pop
      node = self.primary_node
707 a8083063 Iustin Pop
708 a8083063 Iustin Pop
    if lvmap is None:
709 a8083063 Iustin Pop
      lvmap = { node : [] }
710 a8083063 Iustin Pop
      ret = lvmap
711 a8083063 Iustin Pop
    else:
712 a8083063 Iustin Pop
      if not node in lvmap:
713 a8083063 Iustin Pop
        lvmap[node] = []
714 a8083063 Iustin Pop
      ret = None
715 a8083063 Iustin Pop
716 a8083063 Iustin Pop
    if not devs:
717 a8083063 Iustin Pop
      devs = self.disks
718 a8083063 Iustin Pop
719 a8083063 Iustin Pop
    for dev in devs:
720 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
721 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
722 a8083063 Iustin Pop
723 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
724 a8083063 Iustin Pop
        if dev.children:
725 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
726 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
727 a8083063 Iustin Pop
728 a8083063 Iustin Pop
      elif dev.children:
729 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
730 a8083063 Iustin Pop
731 a8083063 Iustin Pop
    return ret
732 a8083063 Iustin Pop
733 ad24e046 Iustin Pop
  def FindDisk(self, idx):
734 ad24e046 Iustin Pop
    """Find a disk given having a specified index.
735 644eeef9 Iustin Pop

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

738 ad24e046 Iustin Pop
    @type idx: int
739 ad24e046 Iustin Pop
    @param idx: the disk index
740 ad24e046 Iustin Pop
    @rtype: L{Disk}
741 ad24e046 Iustin Pop
    @return: the corresponding disk
742 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
743 644eeef9 Iustin Pop

744 ad24e046 Iustin Pop
    """
745 ad24e046 Iustin Pop
    try:
746 ad24e046 Iustin Pop
      idx = int(idx)
747 ad24e046 Iustin Pop
      return self.disks[idx]
748 ad24e046 Iustin Pop
    except ValueError, err:
749 debac808 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err),
750 debac808 Iustin Pop
                                 errors.ECODE_INVAL)
751 ad24e046 Iustin Pop
    except IndexError:
752 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
753 debac808 Iustin Pop
                                 " 0 to %d" % (idx, len(self.disks)),
754 debac808 Iustin Pop
                                 errors.ECODE_INVAL)
755 644eeef9 Iustin Pop
756 ff9c047c Iustin Pop
  def ToDict(self):
757 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
758 ff9c047c Iustin Pop

759 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
760 ff9c047c Iustin Pop
    python types.
761 ff9c047c Iustin Pop

762 ff9c047c Iustin Pop
    """
763 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
764 ff9c047c Iustin Pop
765 ff9c047c Iustin Pop
    for attr in "nics", "disks":
766 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
767 ff9c047c Iustin Pop
      if alist:
768 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
769 ff9c047c Iustin Pop
      else:
770 ff9c047c Iustin Pop
        nlist = []
771 ff9c047c Iustin Pop
      bo[attr] = nlist
772 ff9c047c Iustin Pop
    return bo
773 ff9c047c Iustin Pop
774 ff9c047c Iustin Pop
  @classmethod
775 ff9c047c Iustin Pop
  def FromDict(cls, val):
776 ff9c047c Iustin Pop
    """Custom function for instances.
777 ff9c047c Iustin Pop

778 ff9c047c Iustin Pop
    """
779 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
780 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
781 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
782 ff9c047c Iustin Pop
    return obj
783 ff9c047c Iustin Pop
784 90d726a8 Iustin Pop
  def UpgradeConfig(self):
785 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
786 90d726a8 Iustin Pop

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

855 b86a6bcd Guido Trotter
    """
856 c1b42c18 Guido Trotter
    if self.hvparams is None:
857 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
858 c1b42c18 Guido Trotter
    else:
859 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
860 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
861 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
862 c1b42c18 Guido Trotter
863 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
864 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
865 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
866 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
867 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
868 c8fcde47 Guido Trotter
    if migrate_default_bridge:
869 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
870 c8fcde47 Guido Trotter
        self.default_bridge
871 c1b42c18 Guido Trotter
872 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
873 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
874 b86a6bcd Guido Trotter
875 b989b9d9 Ken Wehr
    if self.modify_ssh_setup is None:
876 b989b9d9 Ken Wehr
      self.modify_ssh_setup = True
877 b989b9d9 Ken Wehr
878 9b31ca85 Guido Trotter
    # default_bridge is no longer used it 2.1. The slot is left there to
879 9b31ca85 Guido Trotter
    # support auto-upgrading, but will be removed in 2.2
880 9b31ca85 Guido Trotter
    if self.default_bridge is not None:
881 9b31ca85 Guido Trotter
      self.default_bridge = None
882 9b31ca85 Guido Trotter
883 066f465d Guido Trotter
    # default_hypervisor is just the first enabled one in 2.1
884 066f465d Guido Trotter
    if self.default_hypervisor is not None:
885 016d04b3 Michael Hanselmann
      self.enabled_hypervisors = ([self.default_hypervisor] +
886 066f465d Guido Trotter
        [hvname for hvname in self.enabled_hypervisors
887 016d04b3 Michael Hanselmann
         if hvname != self.default_hypervisor])
888 066f465d Guido Trotter
      self.default_hypervisor = None
889 066f465d Guido Trotter
890 319856a9 Michael Hanselmann
  def ToDict(self):
891 319856a9 Michael Hanselmann
    """Custom function for cluster.
892 319856a9 Michael Hanselmann

893 319856a9 Michael Hanselmann
    """
894 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
895 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
896 319856a9 Michael Hanselmann
    return mydict
897 319856a9 Michael Hanselmann
898 319856a9 Michael Hanselmann
  @classmethod
899 319856a9 Michael Hanselmann
  def FromDict(cls, val):
900 319856a9 Michael Hanselmann
    """Custom function for cluster.
901 319856a9 Michael Hanselmann

902 319856a9 Michael Hanselmann
    """
903 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
904 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
905 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
906 319856a9 Michael Hanselmann
    return obj
907 319856a9 Michael Hanselmann
908 7736a5f2 Iustin Pop
  def FillHV(self, instance, skip_globals=False):
909 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
910 5bf7b5cf Iustin Pop

911 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
912 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
913 7736a5f2 Iustin Pop
    @type skip_globals: boolean
914 7736a5f2 Iustin Pop
    @param skip_globals: if True, the global hypervisor parameters will
915 7736a5f2 Iustin Pop
        not be filled
916 5bf7b5cf Iustin Pop
    @rtype: dict
917 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
918 5bf7b5cf Iustin Pop
        the cluster defaults
919 5bf7b5cf Iustin Pop

920 5bf7b5cf Iustin Pop
    """
921 7736a5f2 Iustin Pop
    if skip_globals:
922 7736a5f2 Iustin Pop
      skip_keys = constants.HVC_GLOBALS
923 7736a5f2 Iustin Pop
    else:
924 7736a5f2 Iustin Pop
      skip_keys = []
925 abe609b2 Guido Trotter
    return FillDict(self.hvparams.get(instance.hypervisor, {}),
926 7736a5f2 Iustin Pop
                    instance.hvparams, skip_keys=skip_keys)
927 5bf7b5cf Iustin Pop
928 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
929 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
930 5bf7b5cf Iustin Pop

931 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
932 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
933 5bf7b5cf Iustin Pop
    @rtype: dict
934 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
935 5bf7b5cf Iustin Pop
        the cluster defaults
936 5bf7b5cf Iustin Pop

937 5bf7b5cf Iustin Pop
    """
938 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
939 7736a5f2 Iustin Pop
                    instance.beparams)
940 5bf7b5cf Iustin Pop
941 5c947f38 Iustin Pop
942 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
943 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
944 96acbc09 Michael Hanselmann
  __slots__ = [
945 96acbc09 Michael Hanselmann
    "dev_path",
946 96acbc09 Michael Hanselmann
    "major",
947 96acbc09 Michael Hanselmann
    "minor",
948 96acbc09 Michael Hanselmann
    "sync_percent",
949 96acbc09 Michael Hanselmann
    "estimated_time",
950 96acbc09 Michael Hanselmann
    "is_degraded",
951 f208978a Michael Hanselmann
    "ldisk_status",
952 96acbc09 Michael Hanselmann
    ]
953 96acbc09 Michael Hanselmann
954 96acbc09 Michael Hanselmann
955 18d750b9 Guido Trotter
class ConfdRequest(ConfigObject):
956 18d750b9 Guido Trotter
  """Object holding a confd request.
957 18d750b9 Guido Trotter

958 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
959 18d750b9 Guido Trotter
  @ivar type: confd query type
960 18d750b9 Guido Trotter
  @ivar query: query request
961 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
962 18d750b9 Guido Trotter

963 18d750b9 Guido Trotter
  """
964 18d750b9 Guido Trotter
  __slots__ = [
965 18d750b9 Guido Trotter
    "protocol",
966 18d750b9 Guido Trotter
    "type",
967 18d750b9 Guido Trotter
    "query",
968 18d750b9 Guido Trotter
    "rsalt",
969 18d750b9 Guido Trotter
    ]
970 18d750b9 Guido Trotter
971 18d750b9 Guido Trotter
972 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
973 18d750b9 Guido Trotter
  """Object holding a confd reply.
974 18d750b9 Guido Trotter

975 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
976 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
977 18d750b9 Guido Trotter
  @ivar answer: confd query reply
978 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
979 18d750b9 Guido Trotter

980 18d750b9 Guido Trotter
  """
981 18d750b9 Guido Trotter
  __slots__ = [
982 18d750b9 Guido Trotter
    "protocol",
983 18d750b9 Guido Trotter
    "status",
984 18d750b9 Guido Trotter
    "answer",
985 18d750b9 Guido Trotter
    "serial",
986 18d750b9 Guido Trotter
    ]
987 18d750b9 Guido Trotter
988 18d750b9 Guido Trotter
989 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
990 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
991 a8083063 Iustin Pop

992 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
993 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
994 a8083063 Iustin Pop
  buffer.
995 a8083063 Iustin Pop

996 a8083063 Iustin Pop
  """
997 a8083063 Iustin Pop
  def Dumps(self):
998 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
999 a8083063 Iustin Pop
    buf = StringIO()
1000 a8083063 Iustin Pop
    self.write(buf)
1001 a8083063 Iustin Pop
    return buf.getvalue()
1002 a8083063 Iustin Pop
1003 a8083063 Iustin Pop
  @staticmethod
1004 a8083063 Iustin Pop
  def Loads(data):
1005 a8083063 Iustin Pop
    """Load data from a string."""
1006 a8083063 Iustin Pop
    buf = StringIO(data)
1007 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
1008 a8083063 Iustin Pop
    cfp.readfp(buf)
1009 a8083063 Iustin Pop
    return cfp