Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 89b70f39

History | View | Annotate | Download (29.6 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 7260cfbe Iustin Pop
# pylint: disable-msg=E0203,W0201
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 7260cfbe Iustin Pop
# W0201: Attribute '%s' defined outside __init__
35 a8083063 Iustin Pop
36 a8083063 Iustin Pop
import ConfigParser
37 5c947f38 Iustin Pop
import re
38 5bf7b5cf Iustin Pop
import copy
39 d5835922 Michael Hanselmann
from cStringIO import StringIO
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
from ganeti import errors
42 5c947f38 Iustin Pop
from ganeti import constants
43 a8083063 Iustin Pop
44 a8083063 Iustin Pop
45 a8083063 Iustin Pop
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
46 abe609b2 Guido Trotter
           "OS", "Node", "Cluster", "FillDict"]
47 a8083063 Iustin Pop
48 d693c864 Iustin Pop
_TIMESTAMPS = ["ctime", "mtime"]
49 e1dcc53a Iustin Pop
_UUID = ["uuid"]
50 96acbc09 Michael Hanselmann
51 e11ddf13 Iustin Pop
def FillDict(defaults_dict, custom_dict, skip_keys=None):
52 29921401 Iustin Pop
  """Basic function to apply settings on top a default dict.
53 abe609b2 Guido Trotter

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

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

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

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

95 a8083063 Iustin Pop
  It has the following properties:
96 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

280 ff9c047c Iustin Pop
    This replaces the tags set with a list.
281 ff9c047c Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

656 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
657 cfcc5c6d Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

962 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
963 18d750b9 Guido Trotter
  @ivar type: confd query type
964 18d750b9 Guido Trotter
  @ivar query: query request
965 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
966 18d750b9 Guido Trotter

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

979 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
980 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
981 18d750b9 Guido Trotter
  @ivar answer: confd query reply
982 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
983 18d750b9 Guido Trotter

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

996 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
997 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
998 a8083063 Iustin Pop
  buffer.
999 a8083063 Iustin Pop

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