Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 5b349fd1

History | View | Annotate | Download (30.2 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 e1dcc53a Iustin Pop
    ] + _TIMESTAMPS + _UUID
868 a8083063 Iustin Pop
869 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
870 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
871 b86a6bcd Guido Trotter

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

916 319856a9 Michael Hanselmann
    """
917 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
918 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
919 319856a9 Michael Hanselmann
    return mydict
920 319856a9 Michael Hanselmann
921 319856a9 Michael Hanselmann
  @classmethod
922 319856a9 Michael Hanselmann
  def FromDict(cls, val):
923 319856a9 Michael Hanselmann
    """Custom function for cluster.
924 319856a9 Michael Hanselmann

925 319856a9 Michael Hanselmann
    """
926 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
927 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
928 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
929 319856a9 Michael Hanselmann
    return obj
930 319856a9 Michael Hanselmann
931 7736a5f2 Iustin Pop
  def FillHV(self, instance, skip_globals=False):
932 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
933 5bf7b5cf Iustin Pop

934 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
935 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
936 7736a5f2 Iustin Pop
    @type skip_globals: boolean
937 7736a5f2 Iustin Pop
    @param skip_globals: if True, the global hypervisor parameters will
938 7736a5f2 Iustin Pop
        not be filled
939 5bf7b5cf Iustin Pop
    @rtype: dict
940 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
941 5bf7b5cf Iustin Pop
        the cluster defaults
942 5bf7b5cf Iustin Pop

943 5bf7b5cf Iustin Pop
    """
944 7736a5f2 Iustin Pop
    if skip_globals:
945 7736a5f2 Iustin Pop
      skip_keys = constants.HVC_GLOBALS
946 7736a5f2 Iustin Pop
    else:
947 7736a5f2 Iustin Pop
      skip_keys = []
948 17463d22 René Nussbaumer
949 17463d22 René Nussbaumer
    # We fill the list from least to most important override
950 17463d22 René Nussbaumer
    fill_stack = [
951 17463d22 René Nussbaumer
      self.hvparams.get(instance.hypervisor, {}),
952 17463d22 René Nussbaumer
      self.os_hvp.get(instance.os, {}).get(instance.hypervisor, {}),
953 17463d22 René Nussbaumer
      instance.hvparams,
954 17463d22 René Nussbaumer
      ]
955 17463d22 René Nussbaumer
956 17463d22 René Nussbaumer
    ret_dict = {}
957 17463d22 René Nussbaumer
    for o_dict in fill_stack:
958 17463d22 René Nussbaumer
      ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
959 17463d22 René Nussbaumer
960 17463d22 René Nussbaumer
    return ret_dict
961 5bf7b5cf Iustin Pop
962 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
963 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
964 5bf7b5cf Iustin Pop

965 a2a24f4c Guido Trotter
    @type instance: L{objects.Instance}
966 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
967 5bf7b5cf Iustin Pop
    @rtype: dict
968 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
969 5bf7b5cf Iustin Pop
        the cluster defaults
970 5bf7b5cf Iustin Pop

971 5bf7b5cf Iustin Pop
    """
972 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
973 7736a5f2 Iustin Pop
                    instance.beparams)
974 5bf7b5cf Iustin Pop
975 5c947f38 Iustin Pop
976 96acbc09 Michael Hanselmann
class BlockDevStatus(ConfigObject):
977 96acbc09 Michael Hanselmann
  """Config object representing the status of a block device."""
978 96acbc09 Michael Hanselmann
  __slots__ = [
979 96acbc09 Michael Hanselmann
    "dev_path",
980 96acbc09 Michael Hanselmann
    "major",
981 96acbc09 Michael Hanselmann
    "minor",
982 96acbc09 Michael Hanselmann
    "sync_percent",
983 96acbc09 Michael Hanselmann
    "estimated_time",
984 96acbc09 Michael Hanselmann
    "is_degraded",
985 f208978a Michael Hanselmann
    "ldisk_status",
986 96acbc09 Michael Hanselmann
    ]
987 96acbc09 Michael Hanselmann
988 96acbc09 Michael Hanselmann
989 18d750b9 Guido Trotter
class ConfdRequest(ConfigObject):
990 18d750b9 Guido Trotter
  """Object holding a confd request.
991 18d750b9 Guido Trotter

992 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
993 18d750b9 Guido Trotter
  @ivar type: confd query type
994 18d750b9 Guido Trotter
  @ivar query: query request
995 18d750b9 Guido Trotter
  @ivar rsalt: requested reply salt
996 18d750b9 Guido Trotter

997 18d750b9 Guido Trotter
  """
998 18d750b9 Guido Trotter
  __slots__ = [
999 18d750b9 Guido Trotter
    "protocol",
1000 18d750b9 Guido Trotter
    "type",
1001 18d750b9 Guido Trotter
    "query",
1002 18d750b9 Guido Trotter
    "rsalt",
1003 18d750b9 Guido Trotter
    ]
1004 18d750b9 Guido Trotter
1005 18d750b9 Guido Trotter
1006 18d750b9 Guido Trotter
class ConfdReply(ConfigObject):
1007 18d750b9 Guido Trotter
  """Object holding a confd reply.
1008 18d750b9 Guido Trotter

1009 18d750b9 Guido Trotter
  @ivar protocol: confd protocol version
1010 18d750b9 Guido Trotter
  @ivar status: reply status code (ok, error)
1011 18d750b9 Guido Trotter
  @ivar answer: confd query reply
1012 18d750b9 Guido Trotter
  @ivar serial: configuration serial number
1013 18d750b9 Guido Trotter

1014 18d750b9 Guido Trotter
  """
1015 18d750b9 Guido Trotter
  __slots__ = [
1016 18d750b9 Guido Trotter
    "protocol",
1017 18d750b9 Guido Trotter
    "status",
1018 18d750b9 Guido Trotter
    "answer",
1019 18d750b9 Guido Trotter
    "serial",
1020 18d750b9 Guido Trotter
    ]
1021 18d750b9 Guido Trotter
1022 18d750b9 Guido Trotter
1023 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
1024 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
1025 a8083063 Iustin Pop

1026 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
1027 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
1028 a8083063 Iustin Pop
  buffer.
1029 a8083063 Iustin Pop

1030 a8083063 Iustin Pop
  """
1031 a8083063 Iustin Pop
  def Dumps(self):
1032 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
1033 a8083063 Iustin Pop
    buf = StringIO()
1034 a8083063 Iustin Pop
    self.write(buf)
1035 a8083063 Iustin Pop
    return buf.getvalue()
1036 a8083063 Iustin Pop
1037 b39bf4bb Guido Trotter
  @classmethod
1038 b39bf4bb Guido Trotter
  def Loads(cls, data):
1039 a8083063 Iustin Pop
    """Load data from a string."""
1040 a8083063 Iustin Pop
    buf = StringIO(data)
1041 b39bf4bb Guido Trotter
    cfp = cls()
1042 a8083063 Iustin Pop
    cfp.readfp(buf)
1043 a8083063 Iustin Pop
    return cfp