Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 196ec587

History | View | Annotate | Download (31 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 adf385c7 Iustin Pop
    if name not in self._all_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 adf385c7 Iustin Pop
    slots = self._all_slots()
119 a8083063 Iustin Pop
    for name in state:
120 adf385c7 Iustin Pop
      if name in slots:
121 a8083063 Iustin Pop
        setattr(self, name, state[name])
122 a8083063 Iustin Pop
123 adf385c7 Iustin Pop
  @classmethod
124 adf385c7 Iustin Pop
  def _all_slots(cls):
125 adf385c7 Iustin Pop
    """Compute the list of all declared slots for a class.
126 adf385c7 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

995 5bf7b5cf Iustin Pop
    """
996 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
997 7736a5f2 Iustin Pop
                    instance.beparams)
998 5bf7b5cf Iustin Pop
999 5c947f38 Iustin Pop
1000 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
1001 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
1002 96acbc09 Michael Hanselmann
  __slots__ = [
1003 96acbc09 Michael Hanselmann
    "dev_path",
1004 96acbc09 Michael Hanselmann
    "major",
1005 96acbc09 Michael Hanselmann
    "minor",
1006 96acbc09 Michael Hanselmann
    "sync_percent",
1007 96acbc09 Michael Hanselmann
    "estimated_time",
1008 96acbc09 Michael Hanselmann
    "is_degraded",
1009 f208978a Michael Hanselmann
    "ldisk_status",
1010 96acbc09 Michael Hanselmann
    ]
1011 96acbc09 Michael Hanselmann
1012 96acbc09 Michael Hanselmann
1013 18d750b9 Guido Trotter
class ConfdRequest(ConfigObject):
1014 18d750b9 Guido Trotter
  """Object holding a confd request.
1015 18d750b9 Guido Trotter

1016 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
1017 18d750b9 Guido Trotter
  @ivar type: confd query type
1018 18d750b9 Guido Trotter
  @ivar query: query request
1019 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
1020 18d750b9 Guido Trotter

1021 18d750b9 Guido Trotter
  """
1022 18d750b9 Guido Trotter
  __slots__ = [
1023 18d750b9 Guido Trotter
    "protocol",
1024 18d750b9 Guido Trotter
    "type",
1025 18d750b9 Guido Trotter
    "query",
1026 18d750b9 Guido Trotter
    "rsalt",
1027 18d750b9 Guido Trotter
    ]
1028 18d750b9 Guido Trotter
1029 18d750b9 Guido Trotter
1030 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
1031 18d750b9 Guido Trotter
  """Object holding a confd reply.
1032 18d750b9 Guido Trotter

1033 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
1034 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
1035 18d750b9 Guido Trotter
  @ivar answer: confd query reply
1036 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
1037 18d750b9 Guido Trotter

1038 18d750b9 Guido Trotter
  """
1039 18d750b9 Guido Trotter
  __slots__ = [
1040 18d750b9 Guido Trotter
    "protocol",
1041 18d750b9 Guido Trotter
    "status",
1042 18d750b9 Guido Trotter
    "answer",
1043 18d750b9 Guido Trotter
    "serial",
1044 18d750b9 Guido Trotter
    ]
1045 18d750b9 Guido Trotter
1046 18d750b9 Guido Trotter
1047 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
1048 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
1049 a8083063 Iustin Pop

1050 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
1051 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
1052 a8083063 Iustin Pop
  buffer.
1053 a8083063 Iustin Pop

1054 a8083063 Iustin Pop
  """
1055 a8083063 Iustin Pop
  def Dumps(self):
1056 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
1057 a8083063 Iustin Pop
    buf = StringIO()
1058 a8083063 Iustin Pop
    self.write(buf)
1059 a8083063 Iustin Pop
    return buf.getvalue()
1060 a8083063 Iustin Pop
1061 b39bf4bb Guido Trotter
  @classmethod
1062 b39bf4bb Guido Trotter
  def Loads(cls, data):
1063 a8083063 Iustin Pop
    """Load data from a string."""
1064 a8083063 Iustin Pop
    buf = StringIO(data)
1065 b39bf4bb Guido Trotter
    cfp = cls()
1066 a8083063 Iustin Pop
    cfp.readfp(buf)
1067 a8083063 Iustin Pop
    return cfp