Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 5d831182

History | View | Annotate | Download (31.7 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Transportable objects for Ganeti.
23 a8083063 Iustin Pop

24 a8083063 Iustin Pop
This module provides small, mostly data-only objects which are safe to
25 a8083063 Iustin Pop
pass to and from external parties.
26 a8083063 Iustin Pop

27 a8083063 Iustin Pop
"""
28 a8083063 Iustin Pop
29 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 8d8d650c Michael Hanselmann
52 e11ddf13 Iustin Pop
def FillDict(defaults_dict, custom_dict, skip_keys=None):
53 29921401 Iustin Pop
  """Basic function to apply settings on top a default dict.
54 abe609b2 Guido Trotter

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

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

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

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

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

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

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

105 a8083063 Iustin Pop
  """
106 a8083063 Iustin Pop
  __slots__ = []
107 a8083063 Iustin Pop
108 a8083063 Iustin Pop
  def __init__(self, **kwargs):
109 319856a9 Michael Hanselmann
    for k, v in kwargs.iteritems():
110 319856a9 Michael Hanselmann
      setattr(self, k, v)
111 a8083063 Iustin Pop
112 a8083063 Iustin Pop
  def __getattr__(self, name):
113 adf385c7 Iustin Pop
    if name not in self._all_slots():
114 3ecf6786 Iustin Pop
      raise AttributeError("Invalid object attribute %s.%s" %
115 3ecf6786 Iustin Pop
                           (type(self).__name__, name))
116 a8083063 Iustin Pop
    return None
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
  def __setstate__(self, state):
119 adf385c7 Iustin Pop
    slots = self._all_slots()
120 a8083063 Iustin Pop
    for name in state:
121 adf385c7 Iustin Pop
      if name in slots:
122 a8083063 Iustin Pop
        setattr(self, name, state[name])
123 a8083063 Iustin Pop
124 adf385c7 Iustin Pop
  @classmethod
125 adf385c7 Iustin Pop
  def _all_slots(cls):
126 adf385c7 Iustin Pop
    """Compute the list of all declared slots for a class.
127 adf385c7 Iustin Pop

128 adf385c7 Iustin Pop
    """
129 adf385c7 Iustin Pop
    slots = []
130 adf385c7 Iustin Pop
    for parent in cls.__mro__:
131 adf385c7 Iustin Pop
      slots.extend(getattr(parent, "__slots__", []))
132 adf385c7 Iustin Pop
    return slots
133 adf385c7 Iustin Pop
134 ff9c047c Iustin Pop
  def ToDict(self):
135 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
136 ff9c047c Iustin Pop

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

143 ff9c047c Iustin Pop
    """
144 4c14965f Guido Trotter
    result = {}
145 adf385c7 Iustin Pop
    for name in self._all_slots():
146 4c14965f Guido Trotter
      value = getattr(self, name, None)
147 4c14965f Guido Trotter
      if value is not None:
148 4c14965f Guido Trotter
        result[name] = value
149 4c14965f Guido Trotter
    return result
150 4c14965f Guido Trotter
151 4c14965f Guido Trotter
  __getstate__ = ToDict
152 ff9c047c Iustin Pop
153 ff9c047c Iustin Pop
  @classmethod
154 ff9c047c Iustin Pop
  def FromDict(cls, val):
155 ff9c047c Iustin Pop
    """Create an object from a dictionary.
156 ff9c047c Iustin Pop

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

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

165 ff9c047c Iustin Pop
    """
166 ff9c047c Iustin Pop
    if not isinstance(val, dict):
167 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
168 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
169 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
170 7260cfbe Iustin Pop
    obj = cls(**val_str) # pylint: disable-msg=W0142
171 ff9c047c Iustin Pop
    return obj
172 ff9c047c Iustin Pop
173 ff9c047c Iustin Pop
  @staticmethod
174 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
175 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
176 ff9c047c Iustin Pop

177 ff9c047c Iustin Pop
    This method converts a container with elements derived from
178 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
179 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
180 ff9c047c Iustin Pop

181 ff9c047c Iustin Pop
    """
182 ff9c047c Iustin Pop
    if isinstance(container, dict):
183 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
184 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
185 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
186 ff9c047c Iustin Pop
    else:
187 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
188 ff9c047c Iustin Pop
                      type(container))
189 ff9c047c Iustin Pop
    return ret
190 ff9c047c Iustin Pop
191 ff9c047c Iustin Pop
  @staticmethod
192 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
193 ff9c047c Iustin Pop
    """Convert a container from standard python types.
194 ff9c047c Iustin Pop

195 ff9c047c Iustin Pop
    This method converts a container with standard python types to
196 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
197 ff9c047c Iustin Pop
    keys, only the values.
198 ff9c047c Iustin Pop

199 ff9c047c Iustin Pop
    """
200 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
201 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
202 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
203 ff9c047c Iustin Pop
    if c_type is dict:
204 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
205 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
206 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
207 ff9c047c Iustin Pop
    else:
208 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
209 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
210 ff9c047c Iustin Pop
    return ret
211 ff9c047c Iustin Pop
212 e8d563f3 Iustin Pop
  def Copy(self):
213 e8d563f3 Iustin Pop
    """Makes a deep copy of the current object and its children.
214 e8d563f3 Iustin Pop

215 e8d563f3 Iustin Pop
    """
216 e8d563f3 Iustin Pop
    dict_form = self.ToDict()
217 e8d563f3 Iustin Pop
    clone_obj = self.__class__.FromDict(dict_form)
218 e8d563f3 Iustin Pop
    return clone_obj
219 e8d563f3 Iustin Pop
220 ff9c047c Iustin Pop
  def __repr__(self):
221 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
222 ff9c047c Iustin Pop
    return repr(self.ToDict())
223 ff9c047c Iustin Pop
224 560428be Guido Trotter
  def UpgradeConfig(self):
225 560428be Guido Trotter
    """Fill defaults for missing configuration values.
226 560428be Guido Trotter

227 90d726a8 Iustin Pop
    This method will be called at configuration load time, and its
228 90d726a8 Iustin Pop
    implementation will be object dependent.
229 560428be Guido Trotter

230 560428be Guido Trotter
    """
231 560428be Guido Trotter
    pass
232 560428be Guido Trotter
233 a8083063 Iustin Pop
234 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
235 5c947f38 Iustin Pop
  """An generic class supporting tags.
236 5c947f38 Iustin Pop

237 5c947f38 Iustin Pop
  """
238 154b9580 Balazs Lecz
  __slots__ = ["tags"]
239 b5e5632e Iustin Pop
  VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
240 2057f6c7 Iustin Pop
241 b5e5632e Iustin Pop
  @classmethod
242 b5e5632e Iustin Pop
  def ValidateTag(cls, tag):
243 5c947f38 Iustin Pop
    """Check if a tag is valid.
244 5c947f38 Iustin Pop

245 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
246 5c947f38 Iustin Pop
    function has no return value.
247 5c947f38 Iustin Pop

248 5c947f38 Iustin Pop
    """
249 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
250 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
251 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
252 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
253 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
254 5c947f38 Iustin Pop
    if not tag:
255 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
256 b5e5632e Iustin Pop
    if not cls.VALID_TAG_RE.match(tag):
257 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
258 5c947f38 Iustin Pop
259 5c947f38 Iustin Pop
  def GetTags(self):
260 5c947f38 Iustin Pop
    """Return the tags list.
261 5c947f38 Iustin Pop

262 5c947f38 Iustin Pop
    """
263 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
264 5c947f38 Iustin Pop
    if tags is None:
265 5c947f38 Iustin Pop
      tags = self.tags = set()
266 5c947f38 Iustin Pop
    return tags
267 5c947f38 Iustin Pop
268 5c947f38 Iustin Pop
  def AddTag(self, tag):
269 5c947f38 Iustin Pop
    """Add a new tag.
270 5c947f38 Iustin Pop

271 5c947f38 Iustin Pop
    """
272 5c947f38 Iustin Pop
    self.ValidateTag(tag)
273 5c947f38 Iustin Pop
    tags = self.GetTags()
274 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
275 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
276 5c947f38 Iustin Pop
    self.GetTags().add(tag)
277 5c947f38 Iustin Pop
278 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
279 5c947f38 Iustin Pop
    """Remove a tag.
280 5c947f38 Iustin Pop

281 5c947f38 Iustin Pop
    """
282 5c947f38 Iustin Pop
    self.ValidateTag(tag)
283 5c947f38 Iustin Pop
    tags = self.GetTags()
284 5c947f38 Iustin Pop
    try:
285 5c947f38 Iustin Pop
      tags.remove(tag)
286 5c947f38 Iustin Pop
    except KeyError:
287 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
288 5c947f38 Iustin Pop
289 ff9c047c Iustin Pop
  def ToDict(self):
290 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
291 ff9c047c Iustin Pop

292 ff9c047c Iustin Pop
    This replaces the tags set with a list.
293 ff9c047c Iustin Pop

294 ff9c047c Iustin Pop
    """
295 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
296 ff9c047c Iustin Pop
297 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
298 ff9c047c Iustin Pop
    if isinstance(tags, set):
299 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
300 ff9c047c Iustin Pop
    return bo
301 ff9c047c Iustin Pop
302 ff9c047c Iustin Pop
  @classmethod
303 ff9c047c Iustin Pop
  def FromDict(cls, val):
304 ff9c047c Iustin Pop
    """Custom function for instances.
305 ff9c047c Iustin Pop

306 ff9c047c Iustin Pop
    """
307 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
308 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
309 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
310 ff9c047c Iustin Pop
    return obj
311 ff9c047c Iustin Pop
312 5c947f38 Iustin Pop
313 a8083063 Iustin Pop
class ConfigData(ConfigObject):
314 a8083063 Iustin Pop
  """Top-level config object."""
315 016d04b3 Michael Hanselmann
  __slots__ = (["version", "cluster", "nodes", "instances", "serial_no"] +
316 016d04b3 Michael Hanselmann
               _TIMESTAMPS)
317 a8083063 Iustin Pop
318 ff9c047c Iustin Pop
  def ToDict(self):
319 ff9c047c Iustin Pop
    """Custom function for top-level config data.
320 ff9c047c Iustin Pop

321 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
322 ff9c047c Iustin Pop
    with standard python types.
323 ff9c047c Iustin Pop

324 ff9c047c Iustin Pop
    """
325 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
326 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
327 ff9c047c Iustin Pop
    for key in "nodes", "instances":
328 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
329 ff9c047c Iustin Pop
330 ff9c047c Iustin Pop
    return mydict
331 ff9c047c Iustin Pop
332 ff9c047c Iustin Pop
  @classmethod
333 ff9c047c Iustin Pop
  def FromDict(cls, val):
334 ff9c047c Iustin Pop
    """Custom function for top-level config data
335 ff9c047c Iustin Pop

336 ff9c047c Iustin Pop
    """
337 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
338 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
339 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
340 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
341 ff9c047c Iustin Pop
    return obj
342 ff9c047c Iustin Pop
343 90d726a8 Iustin Pop
  def UpgradeConfig(self):
344 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
345 90d726a8 Iustin Pop

346 90d726a8 Iustin Pop
    """
347 90d726a8 Iustin Pop
    self.cluster.UpgradeConfig()
348 90d726a8 Iustin Pop
    for node in self.nodes.values():
349 90d726a8 Iustin Pop
      node.UpgradeConfig()
350 90d726a8 Iustin Pop
    for instance in self.instances.values():
351 90d726a8 Iustin Pop
      instance.UpgradeConfig()
352 90d726a8 Iustin Pop
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
class NIC(ConfigObject):
355 a8083063 Iustin Pop
  """Config object representing a network card."""
356 13f1af63 Guido Trotter
  __slots__ = ["mac", "ip", "bridge", "nicparams"]
357 a8083063 Iustin Pop
358 255e19d4 Guido Trotter
  @classmethod
359 255e19d4 Guido Trotter
  def CheckParameterSyntax(cls, nicparams):
360 255e19d4 Guido Trotter
    """Check the given parameters for validity.
361 255e19d4 Guido Trotter

362 255e19d4 Guido Trotter
    @type nicparams:  dict
363 255e19d4 Guido Trotter
    @param nicparams: dictionary with parameter names/value
364 255e19d4 Guido Trotter
    @raise errors.ConfigurationError: when a parameter is not valid
365 255e19d4 Guido Trotter

366 255e19d4 Guido Trotter
    """
367 255e19d4 Guido Trotter
    if nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES:
368 255e19d4 Guido Trotter
      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
369 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
370 255e19d4 Guido Trotter
371 0c9d32c1 Guido Trotter
    if (nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED and
372 255e19d4 Guido Trotter
        not nicparams[constants.NIC_LINK]):
373 255e19d4 Guido Trotter
      err = "Missing bridged nic link"
374 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
375 255e19d4 Guido Trotter
376 13f1af63 Guido Trotter
  def UpgradeConfig(self):
377 13f1af63 Guido Trotter
    """Fill defaults for missing configuration values.
378 13f1af63 Guido Trotter

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

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

415 e51db2a6 Iustin Pop
    @warning: The path returned is not a normalized pathname; callers
416 e51db2a6 Iustin Pop
        should check that it is a valid path.
417 e51db2a6 Iustin Pop

418 222f2dd5 Iustin Pop
    """
419 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
420 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
421 222f2dd5 Iustin Pop
    return None
422 222f2dd5 Iustin Pop
423 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
424 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
425 fc1dc9d7 Iustin Pop

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

430 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
431 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
432 fc1dc9d7 Iustin Pop
    -1.
433 fc1dc9d7 Iustin Pop

434 fc1dc9d7 Iustin Pop
    """
435 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
436 fc1dc9d7 Iustin Pop
      return 0
437 fc1dc9d7 Iustin Pop
    return -1
438 fc1dc9d7 Iustin Pop
439 a8083063 Iustin Pop
  def GetNodes(self, node):
440 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
441 a8083063 Iustin Pop

442 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
443 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
444 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
445 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
446 a8083063 Iustin Pop

447 a8083063 Iustin Pop
    """
448 00fb8246 Michael Hanselmann
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
449 a8083063 Iustin Pop
      result = [node]
450 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
451 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
452 a8083063 Iustin Pop
      if node not in result:
453 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
454 a8083063 Iustin Pop
    else:
455 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
456 a8083063 Iustin Pop
    return result
457 a8083063 Iustin Pop
458 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
459 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
460 a8083063 Iustin Pop

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

467 a8083063 Iustin Pop
    """
468 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
469 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
470 a8083063 Iustin Pop
    if not self.children:
471 a8083063 Iustin Pop
      # leaf device
472 a8083063 Iustin Pop
      return result
473 a8083063 Iustin Pop
    for node in my_nodes:
474 a8083063 Iustin Pop
      for child in self.children:
475 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
476 a8083063 Iustin Pop
        if len(child_result) == 1:
477 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
478 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
479 a8083063 Iustin Pop
          # own entry for this node describes it completely
480 a8083063 Iustin Pop
          continue
481 a8083063 Iustin Pop
        else:
482 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
483 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
484 a8083063 Iustin Pop
          # one of its descendants
485 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
486 a8083063 Iustin Pop
            if subnode not in my_nodes:
487 a8083063 Iustin Pop
              result.append((subnode, subdisk))
488 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
489 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
490 a8083063 Iustin Pop
            # be different)
491 a8083063 Iustin Pop
    return result
492 a8083063 Iustin Pop
493 acec9d51 Iustin Pop
  def RecordGrow(self, amount):
494 acec9d51 Iustin Pop
    """Update the size of this disk after growth.
495 acec9d51 Iustin Pop

496 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
497 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
498 acec9d51 Iustin Pop
    actual algorithms from bdev.
499 acec9d51 Iustin Pop

500 acec9d51 Iustin Pop
    """
501 2c42c5df Guido Trotter
    if self.dev_type == constants.LD_LV or self.dev_type == constants.LD_FILE:
502 acec9d51 Iustin Pop
      self.size += amount
503 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
504 acec9d51 Iustin Pop
      if self.children:
505 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
506 acec9d51 Iustin Pop
      self.size += amount
507 acec9d51 Iustin Pop
    else:
508 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
509 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
510 acec9d51 Iustin Pop
511 a805ec18 Iustin Pop
  def UnsetSize(self):
512 a805ec18 Iustin Pop
    """Sets recursively the size to zero for the disk and its children.
513 a805ec18 Iustin Pop

514 a805ec18 Iustin Pop
    """
515 a805ec18 Iustin Pop
    if self.children:
516 a805ec18 Iustin Pop
      for child in self.children:
517 a805ec18 Iustin Pop
        child.UnsetSize()
518 a805ec18 Iustin Pop
    self.size = 0
519 a805ec18 Iustin Pop
520 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
521 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
522 0402302c Iustin Pop

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

525 0402302c Iustin Pop
    The routine descends down and updates its children also, because
526 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
527 0402302c Iustin Pop
    node.
528 0402302c Iustin Pop

529 0402302c Iustin Pop
    Arguments:
530 0402302c Iustin Pop
      - target_node: the node we wish to configure for
531 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
532 0402302c Iustin Pop

533 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
534 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
535 0402302c Iustin Pop
    in the disk tree.
536 0402302c Iustin Pop

537 0402302c Iustin Pop
    """
538 0402302c Iustin Pop
    if self.children:
539 0402302c Iustin Pop
      for child in self.children:
540 0402302c Iustin Pop
        child.SetPhysicalID(target_node, nodes_ip)
541 0402302c Iustin Pop
542 0402302c Iustin Pop
    if self.logical_id is None and self.physical_id is not None:
543 0402302c Iustin Pop
      return
544 0402302c Iustin Pop
    if self.dev_type in constants.LDS_DRBD:
545 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = self.logical_id
546 0402302c Iustin Pop
      if target_node not in (pnode, snode):
547 0402302c Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
548 0402302c Iustin Pop
                                        target_node)
549 0402302c Iustin Pop
      pnode_ip = nodes_ip.get(pnode, None)
550 0402302c Iustin Pop
      snode_ip = nodes_ip.get(snode, None)
551 0402302c Iustin Pop
      if pnode_ip is None or snode_ip is None:
552 0402302c Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
553 0402302c Iustin Pop
                                        " for %s" % str(self))
554 ffa1c0dc Iustin Pop
      p_data = (pnode_ip, port)
555 ffa1c0dc Iustin Pop
      s_data = (snode_ip, port)
556 0402302c Iustin Pop
      if pnode == target_node:
557 f9518d38 Iustin Pop
        self.physical_id = p_data + s_data + (pminor, secret)
558 0402302c Iustin Pop
      else: # it must be secondary, we tested above
559 f9518d38 Iustin Pop
        self.physical_id = s_data + p_data + (sminor, secret)
560 0402302c Iustin Pop
    else:
561 0402302c Iustin Pop
      self.physical_id = self.logical_id
562 0402302c Iustin Pop
    return
563 0402302c Iustin Pop
564 ff9c047c Iustin Pop
  def ToDict(self):
565 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
566 ff9c047c Iustin Pop

567 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
568 ff9c047c Iustin Pop
    standard python types.
569 ff9c047c Iustin Pop

570 ff9c047c Iustin Pop
    """
571 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
572 ff9c047c Iustin Pop
573 ff9c047c Iustin Pop
    for attr in ("children",):
574 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
575 ff9c047c Iustin Pop
      if alist:
576 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
577 ff9c047c Iustin Pop
    return bo
578 ff9c047c Iustin Pop
579 ff9c047c Iustin Pop
  @classmethod
580 ff9c047c Iustin Pop
  def FromDict(cls, val):
581 ff9c047c Iustin Pop
    """Custom function for Disks
582 ff9c047c Iustin Pop

583 ff9c047c Iustin Pop
    """
584 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
585 ff9c047c Iustin Pop
    if obj.children:
586 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
587 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
588 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
589 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
590 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
591 f9518d38 Iustin Pop
    if obj.dev_type in constants.LDS_DRBD:
592 f9518d38 Iustin Pop
      # we need a tuple of length six here
593 f9518d38 Iustin Pop
      if len(obj.logical_id) < 6:
594 f9518d38 Iustin Pop
        obj.logical_id += (None,) * (6 - len(obj.logical_id))
595 ff9c047c Iustin Pop
    return obj
596 ff9c047c Iustin Pop
597 65a15336 Iustin Pop
  def __str__(self):
598 65a15336 Iustin Pop
    """Custom str() formatter for disks.
599 65a15336 Iustin Pop

600 65a15336 Iustin Pop
    """
601 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
602 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
603 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
604 89f28b76 Iustin Pop
      node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
605 00fb8246 Michael Hanselmann
      val = "<DRBD8("
606 073ca59e Iustin Pop
      if self.physical_id is None:
607 073ca59e Iustin Pop
        phy = "unconfigured"
608 073ca59e Iustin Pop
      else:
609 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
610 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
611 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
612 073ca59e Iustin Pop
613 89f28b76 Iustin Pop
      val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
614 89f28b76 Iustin Pop
              (node_a, minor_a, node_b, minor_b, port, phy))
615 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
616 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
617 65a15336 Iustin Pop
      else:
618 65a15336 Iustin Pop
        val += "no local storage"
619 65a15336 Iustin Pop
    else:
620 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
621 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
622 65a15336 Iustin Pop
    if self.iv_name is None:
623 65a15336 Iustin Pop
      val += ", not visible"
624 65a15336 Iustin Pop
    else:
625 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
626 fd965830 Iustin Pop
    if isinstance(self.size, int):
627 fd965830 Iustin Pop
      val += ", size=%dm)>" % self.size
628 fd965830 Iustin Pop
    else:
629 fd965830 Iustin Pop
      val += ", size='%s')>" % (self.size,)
630 65a15336 Iustin Pop
    return val
631 65a15336 Iustin Pop
632 332d0e37 Iustin Pop
  def Verify(self):
633 332d0e37 Iustin Pop
    """Checks that this disk is correctly configured.
634 332d0e37 Iustin Pop

635 332d0e37 Iustin Pop
    """
636 7c4d6c7b Michael Hanselmann
    all_errors = []
637 332d0e37 Iustin Pop
    if self.mode not in constants.DISK_ACCESS_SET:
638 7c4d6c7b Michael Hanselmann
      all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
639 7c4d6c7b Michael Hanselmann
    return all_errors
640 332d0e37 Iustin Pop
641 90d726a8 Iustin Pop
  def UpgradeConfig(self):
642 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
643 90d726a8 Iustin Pop

644 90d726a8 Iustin Pop
    """
645 90d726a8 Iustin Pop
    if self.children:
646 90d726a8 Iustin Pop
      for child in self.children:
647 90d726a8 Iustin Pop
        child.UpgradeConfig()
648 90d726a8 Iustin Pop
    # add here config upgrade for this disk
649 90d726a8 Iustin Pop
650 a8083063 Iustin Pop
651 ec29fe40 Iustin Pop
class Instance(TaggableObject):
652 a8083063 Iustin Pop
  """Config object representing an instance."""
653 154b9580 Balazs Lecz
  __slots__ = [
654 a8083063 Iustin Pop
    "name",
655 a8083063 Iustin Pop
    "primary_node",
656 a8083063 Iustin Pop
    "os",
657 e69d05fd Iustin Pop
    "hypervisor",
658 5bf7b5cf Iustin Pop
    "hvparams",
659 5bf7b5cf Iustin Pop
    "beparams",
660 0d68c45d Iustin Pop
    "admin_up",
661 a8083063 Iustin Pop
    "nics",
662 a8083063 Iustin Pop
    "disks",
663 a8083063 Iustin Pop
    "disk_template",
664 58acb49d Alexander Schreiber
    "network_port",
665 be1fa613 Iustin Pop
    "serial_no",
666 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
667 a8083063 Iustin Pop
668 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
669 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
670 a8083063 Iustin Pop

671 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
672 cfcc5c6d Iustin Pop

673 cfcc5c6d Iustin Pop
    """
674 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
675 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
676 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
677 cfcc5c6d Iustin Pop
678 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
679 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
680 cfcc5c6d Iustin Pop
681 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
682 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
683 cfcc5c6d Iustin Pop

684 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
685 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
686 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
687 a8083063 Iustin Pop
    dynamically.
688 a8083063 Iustin Pop

689 a8083063 Iustin Pop
    """
690 cfcc5c6d Iustin Pop
    def _Helper(nodes, device):
691 cfcc5c6d Iustin Pop
      """Recursively computes nodes given a top device."""
692 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
693 cfcc5c6d Iustin Pop
        nodea, nodeb = device.logical_id[:2]
694 cfcc5c6d Iustin Pop
        nodes.add(nodea)
695 cfcc5c6d Iustin Pop
        nodes.add(nodeb)
696 a8083063 Iustin Pop
      if device.children:
697 a8083063 Iustin Pop
        for child in device.children:
698 cfcc5c6d Iustin Pop
          _Helper(nodes, child)
699 a8083063 Iustin Pop
700 cfcc5c6d Iustin Pop
    all_nodes = set()
701 99c7b2a1 Iustin Pop
    all_nodes.add(self.primary_node)
702 a8083063 Iustin Pop
    for device in self.disks:
703 cfcc5c6d Iustin Pop
      _Helper(all_nodes, device)
704 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
705 a8083063 Iustin Pop
706 cfcc5c6d Iustin Pop
  all_nodes = property(_ComputeAllNodes, None, None,
707 cfcc5c6d Iustin Pop
                       "List of all nodes of the instance")
708 a8083063 Iustin Pop
709 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
710 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
711 a8083063 Iustin Pop

712 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
713 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
714 a8083063 Iustin Pop

715 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
716 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
717 a8083063 Iustin Pop

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

721 a8083063 Iustin Pop
    """
722 a8083063 Iustin Pop
    if node == None:
723 a8083063 Iustin Pop
      node = self.primary_node
724 a8083063 Iustin Pop
725 a8083063 Iustin Pop
    if lvmap is None:
726 a8083063 Iustin Pop
      lvmap = { node : [] }
727 a8083063 Iustin Pop
      ret = lvmap
728 a8083063 Iustin Pop
    else:
729 a8083063 Iustin Pop
      if not node in lvmap:
730 a8083063 Iustin Pop
        lvmap[node] = []
731 a8083063 Iustin Pop
      ret = None
732 a8083063 Iustin Pop
733 a8083063 Iustin Pop
    if not devs:
734 a8083063 Iustin Pop
      devs = self.disks
735 a8083063 Iustin Pop
736 a8083063 Iustin Pop
    for dev in devs:
737 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
738 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
739 a8083063 Iustin Pop
740 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
741 a8083063 Iustin Pop
        if dev.children:
742 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
743 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
744 a8083063 Iustin Pop
745 a8083063 Iustin Pop
      elif dev.children:
746 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
747 a8083063 Iustin Pop
748 a8083063 Iustin Pop
    return ret
749 a8083063 Iustin Pop
750 ad24e046 Iustin Pop
  def FindDisk(self, idx):
751 ad24e046 Iustin Pop
    """Find a disk given having a specified index.
752 644eeef9 Iustin Pop

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

755 ad24e046 Iustin Pop
    @type idx: int
756 ad24e046 Iustin Pop
    @param idx: the disk index
757 ad24e046 Iustin Pop
    @rtype: L{Disk}
758 ad24e046 Iustin Pop
    @return: the corresponding disk
759 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
760 644eeef9 Iustin Pop

761 ad24e046 Iustin Pop
    """
762 ad24e046 Iustin Pop
    try:
763 ad24e046 Iustin Pop
      idx = int(idx)
764 ad24e046 Iustin Pop
      return self.disks[idx]
765 691744c4 Iustin Pop
    except (TypeError, ValueError), err:
766 debac808 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err),
767 debac808 Iustin Pop
                                 errors.ECODE_INVAL)
768 ad24e046 Iustin Pop
    except IndexError:
769 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
770 debac808 Iustin Pop
                                 " 0 to %d" % (idx, len(self.disks)),
771 debac808 Iustin Pop
                                 errors.ECODE_INVAL)
772 644eeef9 Iustin Pop
773 ff9c047c Iustin Pop
  def ToDict(self):
774 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
775 ff9c047c Iustin Pop

776 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
777 ff9c047c Iustin Pop
    python types.
778 ff9c047c Iustin Pop

779 ff9c047c Iustin Pop
    """
780 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
781 ff9c047c Iustin Pop
782 ff9c047c Iustin Pop
    for attr in "nics", "disks":
783 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
784 ff9c047c Iustin Pop
      if alist:
785 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
786 ff9c047c Iustin Pop
      else:
787 ff9c047c Iustin Pop
        nlist = []
788 ff9c047c Iustin Pop
      bo[attr] = nlist
789 ff9c047c Iustin Pop
    return bo
790 ff9c047c Iustin Pop
791 ff9c047c Iustin Pop
  @classmethod
792 ff9c047c Iustin Pop
  def FromDict(cls, val):
793 ff9c047c Iustin Pop
    """Custom function for instances.
794 ff9c047c Iustin Pop

795 ff9c047c Iustin Pop
    """
796 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
797 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
798 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
799 ff9c047c Iustin Pop
    return obj
800 ff9c047c Iustin Pop
801 90d726a8 Iustin Pop
  def UpgradeConfig(self):
802 90d726a8 Iustin Pop
    """Fill defaults for missing configuration values.
803 90d726a8 Iustin Pop

804 90d726a8 Iustin Pop
    """
805 90d726a8 Iustin Pop
    for nic in self.nics:
806 90d726a8 Iustin Pop
      nic.UpgradeConfig()
807 90d726a8 Iustin Pop
    for disk in self.disks:
808 90d726a8 Iustin Pop
      disk.UpgradeConfig()
809 7736a5f2 Iustin Pop
    if self.hvparams:
810 7736a5f2 Iustin Pop
      for key in constants.HVC_GLOBALS:
811 7736a5f2 Iustin Pop
        try:
812 7736a5f2 Iustin Pop
          del self.hvparams[key]
813 7736a5f2 Iustin Pop
        except KeyError:
814 7736a5f2 Iustin Pop
          pass
815 90d726a8 Iustin Pop
816 a8083063 Iustin Pop
817 a8083063 Iustin Pop
class OS(ConfigObject):
818 a8083063 Iustin Pop
  """Config object representing an operating system."""
819 a8083063 Iustin Pop
  __slots__ = [
820 a8083063 Iustin Pop
    "name",
821 a8083063 Iustin Pop
    "path",
822 082a7f91 Guido Trotter
    "api_versions",
823 a8083063 Iustin Pop
    "create_script",
824 a8083063 Iustin Pop
    "export_script",
825 386b57af Iustin Pop
    "import_script",
826 386b57af Iustin Pop
    "rename_script",
827 6d79896b Guido Trotter
    "supported_variants",
828 a8083063 Iustin Pop
    ]
829 a8083063 Iustin Pop
830 7c0d6283 Michael Hanselmann
831 ec29fe40 Iustin Pop
class Node(TaggableObject):
832 a8083063 Iustin Pop
  """Config object representing a node."""
833 154b9580 Balazs Lecz
  __slots__ = [
834 ec29fe40 Iustin Pop
    "name",
835 ec29fe40 Iustin Pop
    "primary_ip",
836 ec29fe40 Iustin Pop
    "secondary_ip",
837 be1fa613 Iustin Pop
    "serial_no",
838 8b8b8b81 Iustin Pop
    "master_candidate",
839 fc0fe88c Iustin Pop
    "offline",
840 af64c0ea Iustin Pop
    "drained",
841 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
842 a8083063 Iustin Pop
843 a8083063 Iustin Pop
844 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
845 a8083063 Iustin Pop
  """Config object representing the cluster."""
846 154b9580 Balazs Lecz
  __slots__ = [
847 a8083063 Iustin Pop
    "serial_no",
848 a8083063 Iustin Pop
    "rsahostkeypub",
849 a8083063 Iustin Pop
    "highest_used_port",
850 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
851 a8083063 Iustin Pop
    "mac_prefix",
852 a8083063 Iustin Pop
    "volume_group_name",
853 a8083063 Iustin Pop
    "default_bridge",
854 02691904 Alexander Schreiber
    "default_hypervisor",
855 f6bd6e98 Michael Hanselmann
    "master_node",
856 f6bd6e98 Michael Hanselmann
    "master_ip",
857 f6bd6e98 Michael Hanselmann
    "master_netdev",
858 f6bd6e98 Michael Hanselmann
    "cluster_name",
859 f6bd6e98 Michael Hanselmann
    "file_storage_dir",
860 e69d05fd Iustin Pop
    "enabled_hypervisors",
861 5bf7b5cf Iustin Pop
    "hvparams",
862 17463d22 René Nussbaumer
    "os_hvp",
863 5bf7b5cf Iustin Pop
    "beparams",
864 c8fcde47 Guido Trotter
    "nicparams",
865 4b7735f9 Iustin Pop
    "candidate_pool_size",
866 b86a6bcd Guido Trotter
    "modify_etc_hosts",
867 b989b9d9 Ken Wehr
    "modify_ssh_setup",
868 3953242f Iustin Pop
    "maintain_node_health",
869 4437d889 Balazs Lecz
    "uid_pool",
870 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
871 a8083063 Iustin Pop
872 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
873 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
874 b86a6bcd Guido Trotter

875 b86a6bcd Guido Trotter
    """
876 fe267188 Iustin Pop
    # pylint: disable-msg=E0203
877 fe267188 Iustin Pop
    # because these are "defined" via slots, not manually
878 c1b42c18 Guido Trotter
    if self.hvparams is None:
879 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
880 c1b42c18 Guido Trotter
    else:
881 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
882 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
883 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
884 c1b42c18 Guido Trotter
885 17463d22 René Nussbaumer
    # TODO: Figure out if it's better to put this into OS than Cluster
886 17463d22 René Nussbaumer
    if self.os_hvp is None:
887 17463d22 René Nussbaumer
      self.os_hvp = {}
888 17463d22 René Nussbaumer
889 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
890 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
891 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
892 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
893 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
894 c8fcde47 Guido Trotter
    if migrate_default_bridge:
895 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
896 c8fcde47 Guido Trotter
        self.default_bridge
897 c1b42c18 Guido Trotter
898 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
899 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
900 b86a6bcd Guido Trotter
901 b989b9d9 Ken Wehr
    if self.modify_ssh_setup is None:
902 b989b9d9 Ken Wehr
      self.modify_ssh_setup = True
903 b989b9d9 Ken Wehr
904 9b31ca85 Guido Trotter
    # default_bridge is no longer used it 2.1. The slot is left there to
905 9b31ca85 Guido Trotter
    # support auto-upgrading, but will be removed in 2.2
906 9b31ca85 Guido Trotter
    if self.default_bridge is not None:
907 9b31ca85 Guido Trotter
      self.default_bridge = None
908 9b31ca85 Guido Trotter
909 066f465d Guido Trotter
    # default_hypervisor is just the first enabled one in 2.1
910 066f465d Guido Trotter
    if self.default_hypervisor is not None:
911 016d04b3 Michael Hanselmann
      self.enabled_hypervisors = ([self.default_hypervisor] +
912 066f465d Guido Trotter
        [hvname for hvname in self.enabled_hypervisors
913 016d04b3 Michael Hanselmann
         if hvname != self.default_hypervisor])
914 066f465d Guido Trotter
      self.default_hypervisor = None
915 066f465d Guido Trotter
916 3953242f Iustin Pop
    # maintain_node_health added after 2.1.1
917 3953242f Iustin Pop
    if self.maintain_node_health is None:
918 3953242f Iustin Pop
      self.maintain_node_health = False
919 3953242f Iustin Pop
920 4437d889 Balazs Lecz
    if self.uid_pool is None:
921 4437d889 Balazs Lecz
      self.uid_pool = []
922 4437d889 Balazs Lecz
923 319856a9 Michael Hanselmann
  def ToDict(self):
924 319856a9 Michael Hanselmann
    """Custom function for cluster.
925 319856a9 Michael Hanselmann

926 319856a9 Michael Hanselmann
    """
927 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
928 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
929 319856a9 Michael Hanselmann
    return mydict
930 319856a9 Michael Hanselmann
931 319856a9 Michael Hanselmann
  @classmethod
932 319856a9 Michael Hanselmann
  def FromDict(cls, val):
933 319856a9 Michael Hanselmann
    """Custom function for cluster.
934 319856a9 Michael Hanselmann

935 319856a9 Michael Hanselmann
    """
936 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
937 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
938 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
939 319856a9 Michael Hanselmann
    return obj
940 319856a9 Michael Hanselmann
941 d63479b5 Iustin Pop
  def GetHVDefaults(self, hypervisor, os_name=None, skip_keys=None):
942 d63479b5 Iustin Pop
    """Get the default hypervisor parameters for the cluster.
943 d63479b5 Iustin Pop

944 d63479b5 Iustin Pop
    @param hypervisor: the hypervisor name
945 d63479b5 Iustin Pop
    @param os_name: if specified, we'll also update the defaults for this OS
946 d63479b5 Iustin Pop
    @param skip_keys: if passed, list of keys not to use
947 d63479b5 Iustin Pop
    @return: the defaults dict
948 d63479b5 Iustin Pop

949 d63479b5 Iustin Pop
    """
950 d63479b5 Iustin Pop
    if skip_keys is None:
951 d63479b5 Iustin Pop
      skip_keys = []
952 d63479b5 Iustin Pop
953 d63479b5 Iustin Pop
    fill_stack = [self.hvparams.get(hypervisor, {})]
954 d63479b5 Iustin Pop
    if os_name is not None:
955 d63479b5 Iustin Pop
      os_hvp = self.os_hvp.get(os_name, {}).get(hypervisor, {})
956 d63479b5 Iustin Pop
      fill_stack.append(os_hvp)
957 d63479b5 Iustin Pop
958 d63479b5 Iustin Pop
    ret_dict = {}
959 d63479b5 Iustin Pop
    for o_dict in fill_stack:
960 d63479b5 Iustin Pop
      ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
961 d63479b5 Iustin Pop
962 d63479b5 Iustin Pop
    return ret_dict
963 d63479b5 Iustin Pop
964 d63479b5 Iustin Pop
965 7736a5f2 Iustin Pop
  def FillHV(self, instance, skip_globals=False):
966 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
967 5bf7b5cf Iustin Pop

968 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
969 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
970 7736a5f2 Iustin Pop
    @type skip_globals: boolean
971 7736a5f2 Iustin Pop
    @param skip_globals: if True, the global hypervisor parameters will
972 7736a5f2 Iustin Pop
        not be filled
973 5bf7b5cf Iustin Pop
    @rtype: dict
974 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
975 5bf7b5cf Iustin Pop
        the cluster defaults
976 5bf7b5cf Iustin Pop

977 5bf7b5cf Iustin Pop
    """
978 7736a5f2 Iustin Pop
    if skip_globals:
979 7736a5f2 Iustin Pop
      skip_keys = constants.HVC_GLOBALS
980 7736a5f2 Iustin Pop
    else:
981 7736a5f2 Iustin Pop
      skip_keys = []
982 17463d22 René Nussbaumer
983 d63479b5 Iustin Pop
    def_dict = self.GetHVDefaults(instance.hypervisor, instance.os,
984 d63479b5 Iustin Pop
                                  skip_keys=skip_keys)
985 d63479b5 Iustin Pop
    return FillDict(def_dict, instance.hvparams, skip_keys=skip_keys)
986 5bf7b5cf Iustin Pop
987 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
988 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
989 5bf7b5cf Iustin Pop

990 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
991 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
992 5bf7b5cf Iustin Pop
    @rtype: dict
993 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
994 5bf7b5cf Iustin Pop
        the cluster defaults
995 5bf7b5cf Iustin Pop

996 5bf7b5cf Iustin Pop
    """
997 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
998 7736a5f2 Iustin Pop
                    instance.beparams)
999 5bf7b5cf Iustin Pop
1000 5c947f38 Iustin Pop
1001 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
1002 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
1003 96acbc09 Michael Hanselmann
  __slots__ = [
1004 96acbc09 Michael Hanselmann
    "dev_path",
1005 96acbc09 Michael Hanselmann
    "major",
1006 96acbc09 Michael Hanselmann
    "minor",
1007 96acbc09 Michael Hanselmann
    "sync_percent",
1008 96acbc09 Michael Hanselmann
    "estimated_time",
1009 96acbc09 Michael Hanselmann
    "is_degraded",
1010 f208978a Michael Hanselmann
    "ldisk_status",
1011 96acbc09 Michael Hanselmann
    ]
1012 96acbc09 Michael Hanselmann
1013 96acbc09 Michael Hanselmann
1014 2d76b580 Michael Hanselmann
class ImportExportStatus(ConfigObject):
1015 2d76b580 Michael Hanselmann
  """Config object representing the status of an import or export."""
1016 2d76b580 Michael Hanselmann
  __slots__ = [
1017 2d76b580 Michael Hanselmann
    "recent_output",
1018 2d76b580 Michael Hanselmann
    "listen_port",
1019 2d76b580 Michael Hanselmann
    "connected",
1020 c08d76f5 Michael Hanselmann
    "progress_mbytes",
1021 c08d76f5 Michael Hanselmann
    "progress_throughput",
1022 c08d76f5 Michael Hanselmann
    "progress_eta",
1023 c08d76f5 Michael Hanselmann
    "progress_percent",
1024 2d76b580 Michael Hanselmann
    "exit_status",
1025 2d76b580 Michael Hanselmann
    "error_message",
1026 2d76b580 Michael Hanselmann
    ] + _TIMESTAMPS
1027 2d76b580 Michael Hanselmann
1028 2d76b580 Michael Hanselmann
1029 eb630f50 Michael Hanselmann
class ImportExportOptions(ConfigObject):
1030 eb630f50 Michael Hanselmann
  """Options for import/export daemon
1031 eb630f50 Michael Hanselmann

1032 eb630f50 Michael Hanselmann
  @ivar key_name: X509 key name (None for cluster certificate)
1033 eb630f50 Michael Hanselmann
  @ivar ca_pem: Remote peer CA in PEM format (None for cluster certificate)
1034 a5310c2a Michael Hanselmann
  @ivar compress: Compression method (one of L{constants.IEC_ALL})
1035 af1d39b1 Michael Hanselmann
  @ivar magic: Used to ensure the connection goes to the right disk
1036 eb630f50 Michael Hanselmann

1037 eb630f50 Michael Hanselmann
  """
1038 eb630f50 Michael Hanselmann
  __slots__ = [
1039 eb630f50 Michael Hanselmann
    "key_name",
1040 eb630f50 Michael Hanselmann
    "ca_pem",
1041 a5310c2a Michael Hanselmann
    "compress",
1042 af1d39b1 Michael Hanselmann
    "magic",
1043 eb630f50 Michael Hanselmann
    ]
1044 eb630f50 Michael Hanselmann
1045 eb630f50 Michael Hanselmann
1046 18d750b9 Guido Trotter
class ConfdRequest(ConfigObject):
1047 18d750b9 Guido Trotter
  """Object holding a confd request.
1048 18d750b9 Guido Trotter

1049 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
1050 18d750b9 Guido Trotter
  @ivar type: confd query type
1051 18d750b9 Guido Trotter
  @ivar query: query request
1052 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
1053 18d750b9 Guido Trotter

1054 18d750b9 Guido Trotter
  """
1055 18d750b9 Guido Trotter
  __slots__ = [
1056 18d750b9 Guido Trotter
    "protocol",
1057 18d750b9 Guido Trotter
    "type",
1058 18d750b9 Guido Trotter
    "query",
1059 18d750b9 Guido Trotter
    "rsalt",
1060 18d750b9 Guido Trotter
    ]
1061 18d750b9 Guido Trotter
1062 18d750b9 Guido Trotter
1063 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
1064 18d750b9 Guido Trotter
  """Object holding a confd reply.
1065 18d750b9 Guido Trotter

1066 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
1067 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
1068 18d750b9 Guido Trotter
  @ivar answer: confd query reply
1069 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
1070 18d750b9 Guido Trotter

1071 18d750b9 Guido Trotter
  """
1072 18d750b9 Guido Trotter
  __slots__ = [
1073 18d750b9 Guido Trotter
    "protocol",
1074 18d750b9 Guido Trotter
    "status",
1075 18d750b9 Guido Trotter
    "answer",
1076 18d750b9 Guido Trotter
    "serial",
1077 18d750b9 Guido Trotter
    ]
1078 18d750b9 Guido Trotter
1079 18d750b9 Guido Trotter
1080 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
1081 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
1082 a8083063 Iustin Pop

1083 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
1084 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
1085 a8083063 Iustin Pop
  buffer.
1086 a8083063 Iustin Pop

1087 a8083063 Iustin Pop
  """
1088 a8083063 Iustin Pop
  def Dumps(self):
1089 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
1090 a8083063 Iustin Pop
    buf = StringIO()
1091 a8083063 Iustin Pop
    self.write(buf)
1092 a8083063 Iustin Pop
    return buf.getvalue()
1093 a8083063 Iustin Pop
1094 b39bf4bb Guido Trotter
  @classmethod
1095 b39bf4bb Guido Trotter
  def Loads(cls, data):
1096 a8083063 Iustin Pop
    """Load data from a string."""
1097 a8083063 Iustin Pop
    buf = StringIO(data)
1098 b39bf4bb Guido Trotter
    cfp = cls()
1099 a8083063 Iustin Pop
    cfp.readfp(buf)
1100 a8083063 Iustin Pop
    return cfp