Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 6c00d19a

History | View | Annotate | Download (26.8 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 a8083063 Iustin Pop
30 a8083063 Iustin Pop
import ConfigParser
31 5c947f38 Iustin Pop
import re
32 5bf7b5cf Iustin Pop
import copy
33 d5835922 Michael Hanselmann
from cStringIO import StringIO
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
from ganeti import errors
36 5c947f38 Iustin Pop
from ganeti import constants
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
40 abe609b2 Guido Trotter
           "OS", "Node", "Cluster", "FillDict"]
41 a8083063 Iustin Pop
42 abe609b2 Guido Trotter
def FillDict(defaults_dict, custom_dict):
43 abe609b2 Guido Trotter
    """Basic function to apply settings on top a default dict.
44 abe609b2 Guido Trotter

45 abe609b2 Guido Trotter
    @type defaults_dict: dict
46 abe609b2 Guido Trotter
    @param defaults_dict: dictionary holding the default values
47 abe609b2 Guido Trotter
    @type custom_dict: dict
48 abe609b2 Guido Trotter
    @param custom_dict: dictionary holding customized value
49 abe609b2 Guido Trotter
    @rtype: dict
50 abe609b2 Guido Trotter
    @return: dict with the 'full' values
51 abe609b2 Guido Trotter

52 abe609b2 Guido Trotter
    """
53 abe609b2 Guido Trotter
    ret_dict = copy.deepcopy(defaults_dict)
54 abe609b2 Guido Trotter
    ret_dict.update(custom_dict)
55 abe609b2 Guido Trotter
    return ret_dict
56 a8083063 Iustin Pop
57 6e34b628 Guido Trotter
58 6e34b628 Guido Trotter
def UpgradeGroupedParams(target, defaults):
59 6e34b628 Guido Trotter
  """Update all groups for the target parameter.
60 6e34b628 Guido Trotter

61 6e34b628 Guido Trotter
  @type target: dict of dicts
62 6e34b628 Guido Trotter
  @param target: {group: {parameter: value}}
63 6e34b628 Guido Trotter
  @type defaults: dict
64 6e34b628 Guido Trotter
  @param defaults: default parameter values
65 6e34b628 Guido Trotter

66 6e34b628 Guido Trotter
  """
67 6e34b628 Guido Trotter
  if target is None:
68 6e34b628 Guido Trotter
    target = {constants.PP_DEFAULT: defaults}
69 6e34b628 Guido Trotter
  else:
70 6e34b628 Guido Trotter
    for group in target:
71 6e34b628 Guido Trotter
      target[group] = FillDict(defaults, target[group])
72 6e34b628 Guido Trotter
  return target
73 6e34b628 Guido Trotter
74 6e34b628 Guido Trotter
75 a8083063 Iustin Pop
class ConfigObject(object):
76 a8083063 Iustin Pop
  """A generic config object.
77 a8083063 Iustin Pop

78 a8083063 Iustin Pop
  It has the following properties:
79 a8083063 Iustin Pop

80 a8083063 Iustin Pop
    - provides somewhat safe recursive unpickling and pickling for its classes
81 a8083063 Iustin Pop
    - unset attributes which are defined in slots are always returned
82 a8083063 Iustin Pop
      as None instead of raising an error
83 a8083063 Iustin Pop

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

87 a8083063 Iustin Pop
  """
88 a8083063 Iustin Pop
  __slots__ = []
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
  def __init__(self, **kwargs):
91 319856a9 Michael Hanselmann
    for k, v in kwargs.iteritems():
92 319856a9 Michael Hanselmann
      setattr(self, k, v)
93 560428be Guido Trotter
    self.UpgradeConfig()
94 a8083063 Iustin Pop
95 a8083063 Iustin Pop
  def __getattr__(self, name):
96 a8083063 Iustin Pop
    if name not in self.__slots__:
97 3ecf6786 Iustin Pop
      raise AttributeError("Invalid object attribute %s.%s" %
98 3ecf6786 Iustin Pop
                           (type(self).__name__, name))
99 a8083063 Iustin Pop
    return None
100 a8083063 Iustin Pop
101 47c28c5b Michael Hanselmann
  def __setitem__(self, key, value):
102 47c28c5b Michael Hanselmann
    if key not in self.__slots__:
103 3ecf6786 Iustin Pop
      raise KeyError(key)
104 47c28c5b Michael Hanselmann
    setattr(self, key, value)
105 47c28c5b Michael Hanselmann
106 a8083063 Iustin Pop
  def __getstate__(self):
107 a8083063 Iustin Pop
    state = {}
108 a8083063 Iustin Pop
    for name in self.__slots__:
109 a8083063 Iustin Pop
      if hasattr(self, name):
110 a8083063 Iustin Pop
        state[name] = getattr(self, name)
111 a8083063 Iustin Pop
    return state
112 a8083063 Iustin Pop
113 a8083063 Iustin Pop
  def __setstate__(self, state):
114 a8083063 Iustin Pop
    for name in state:
115 a8083063 Iustin Pop
      if name in self.__slots__:
116 a8083063 Iustin Pop
        setattr(self, name, state[name])
117 a8083063 Iustin Pop
118 ff9c047c Iustin Pop
  def ToDict(self):
119 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
120 ff9c047c Iustin Pop

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

127 ff9c047c Iustin Pop
    """
128 ff9c047c Iustin Pop
    return dict([(k, getattr(self, k, None)) for k in self.__slots__])
129 ff9c047c Iustin Pop
130 ff9c047c Iustin Pop
  @classmethod
131 ff9c047c Iustin Pop
  def FromDict(cls, val):
132 ff9c047c Iustin Pop
    """Create an object from a dictionary.
133 ff9c047c Iustin Pop

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

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

142 ff9c047c Iustin Pop
    """
143 ff9c047c Iustin Pop
    if not isinstance(val, dict):
144 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
145 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
146 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
147 319856a9 Michael Hanselmann
    obj = cls(**val_str)
148 ff9c047c Iustin Pop
    return obj
149 ff9c047c Iustin Pop
150 ff9c047c Iustin Pop
  @staticmethod
151 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
152 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
153 ff9c047c Iustin Pop

154 ff9c047c Iustin Pop
    This method converts a container with elements derived from
155 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
156 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
157 ff9c047c Iustin Pop

158 ff9c047c Iustin Pop
    """
159 ff9c047c Iustin Pop
    if isinstance(container, dict):
160 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
161 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
162 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
163 ff9c047c Iustin Pop
    else:
164 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
165 ff9c047c Iustin Pop
                      type(container))
166 ff9c047c Iustin Pop
    return ret
167 ff9c047c Iustin Pop
168 ff9c047c Iustin Pop
  @staticmethod
169 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
170 ff9c047c Iustin Pop
    """Convert a container from standard python types.
171 ff9c047c Iustin Pop

172 ff9c047c Iustin Pop
    This method converts a container with standard python types to
173 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
174 ff9c047c Iustin Pop
    keys, only the values.
175 ff9c047c Iustin Pop

176 ff9c047c Iustin Pop
    """
177 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
178 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
179 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
180 ff9c047c Iustin Pop
    if c_type is dict:
181 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
182 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
183 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
184 ff9c047c Iustin Pop
    else:
185 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
186 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
187 ff9c047c Iustin Pop
    return ret
188 ff9c047c Iustin Pop
189 ff9c047c Iustin Pop
  def __repr__(self):
190 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
191 ff9c047c Iustin Pop
    return repr(self.ToDict())
192 ff9c047c Iustin Pop
193 560428be Guido Trotter
  def UpgradeConfig(self):
194 560428be Guido Trotter
    """Fill defaults for missing configuration values.
195 560428be Guido Trotter

196 560428be Guido Trotter
    This method will be called at object init time, and its implementation will
197 560428be Guido Trotter
    be object dependent.
198 560428be Guido Trotter

199 560428be Guido Trotter
    """
200 560428be Guido Trotter
    pass
201 560428be Guido Trotter
202 a8083063 Iustin Pop
203 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
204 5c947f38 Iustin Pop
  """An generic class supporting tags.
205 5c947f38 Iustin Pop

206 5c947f38 Iustin Pop
  """
207 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
208 2057f6c7 Iustin Pop
209 5c947f38 Iustin Pop
  @staticmethod
210 5c947f38 Iustin Pop
  def ValidateTag(tag):
211 5c947f38 Iustin Pop
    """Check if a tag is valid.
212 5c947f38 Iustin Pop

213 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
214 5c947f38 Iustin Pop
    function has no return value.
215 5c947f38 Iustin Pop

216 5c947f38 Iustin Pop
    """
217 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
218 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
219 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
220 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
221 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
222 5c947f38 Iustin Pop
    if not tag:
223 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
224 28ab6fed Iustin Pop
    if not re.match("^[\w.+*/:-]+$", tag):
225 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
226 5c947f38 Iustin Pop
227 5c947f38 Iustin Pop
  def GetTags(self):
228 5c947f38 Iustin Pop
    """Return the tags list.
229 5c947f38 Iustin Pop

230 5c947f38 Iustin Pop
    """
231 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
232 5c947f38 Iustin Pop
    if tags is None:
233 5c947f38 Iustin Pop
      tags = self.tags = set()
234 5c947f38 Iustin Pop
    return tags
235 5c947f38 Iustin Pop
236 5c947f38 Iustin Pop
  def AddTag(self, tag):
237 5c947f38 Iustin Pop
    """Add a new tag.
238 5c947f38 Iustin Pop

239 5c947f38 Iustin Pop
    """
240 5c947f38 Iustin Pop
    self.ValidateTag(tag)
241 5c947f38 Iustin Pop
    tags = self.GetTags()
242 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
243 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
244 5c947f38 Iustin Pop
    self.GetTags().add(tag)
245 5c947f38 Iustin Pop
246 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
247 5c947f38 Iustin Pop
    """Remove a tag.
248 5c947f38 Iustin Pop

249 5c947f38 Iustin Pop
    """
250 5c947f38 Iustin Pop
    self.ValidateTag(tag)
251 5c947f38 Iustin Pop
    tags = self.GetTags()
252 5c947f38 Iustin Pop
    try:
253 5c947f38 Iustin Pop
      tags.remove(tag)
254 5c947f38 Iustin Pop
    except KeyError:
255 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
256 5c947f38 Iustin Pop
257 ff9c047c Iustin Pop
  def ToDict(self):
258 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
259 ff9c047c Iustin Pop

260 ff9c047c Iustin Pop
    This replaces the tags set with a list.
261 ff9c047c Iustin Pop

262 ff9c047c Iustin Pop
    """
263 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
264 ff9c047c Iustin Pop
265 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
266 ff9c047c Iustin Pop
    if isinstance(tags, set):
267 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
268 ff9c047c Iustin Pop
    return bo
269 ff9c047c Iustin Pop
270 ff9c047c Iustin Pop
  @classmethod
271 ff9c047c Iustin Pop
  def FromDict(cls, val):
272 ff9c047c Iustin Pop
    """Custom function for instances.
273 ff9c047c Iustin Pop

274 ff9c047c Iustin Pop
    """
275 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
276 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
277 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
278 ff9c047c Iustin Pop
    return obj
279 ff9c047c Iustin Pop
280 5c947f38 Iustin Pop
281 a8083063 Iustin Pop
class ConfigData(ConfigObject):
282 a8083063 Iustin Pop
  """Top-level config object."""
283 f6bd6e98 Michael Hanselmann
  __slots__ = ["version", "cluster", "nodes", "instances", "serial_no"]
284 a8083063 Iustin Pop
285 ff9c047c Iustin Pop
  def ToDict(self):
286 ff9c047c Iustin Pop
    """Custom function for top-level config data.
287 ff9c047c Iustin Pop

288 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
289 ff9c047c Iustin Pop
    with standard python types.
290 ff9c047c Iustin Pop

291 ff9c047c Iustin Pop
    """
292 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
293 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
294 ff9c047c Iustin Pop
    for key in "nodes", "instances":
295 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
296 ff9c047c Iustin Pop
297 ff9c047c Iustin Pop
    return mydict
298 ff9c047c Iustin Pop
299 ff9c047c Iustin Pop
  @classmethod
300 ff9c047c Iustin Pop
  def FromDict(cls, val):
301 ff9c047c Iustin Pop
    """Custom function for top-level config data
302 ff9c047c Iustin Pop

303 ff9c047c Iustin Pop
    """
304 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
305 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
306 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
307 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
308 ff9c047c Iustin Pop
    return obj
309 ff9c047c Iustin Pop
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
class NIC(ConfigObject):
312 a8083063 Iustin Pop
  """Config object representing a network card."""
313 13f1af63 Guido Trotter
  __slots__ = ["mac", "ip", "bridge", "nicparams"]
314 a8083063 Iustin Pop
315 255e19d4 Guido Trotter
  @classmethod
316 255e19d4 Guido Trotter
  def CheckParameterSyntax(cls, nicparams):
317 255e19d4 Guido Trotter
    """Check the given parameters for validity.
318 255e19d4 Guido Trotter

319 255e19d4 Guido Trotter
    @type nicparams:  dict
320 255e19d4 Guido Trotter
    @param nicparams: dictionary with parameter names/value
321 255e19d4 Guido Trotter
    @raise errors.ConfigurationError: when a parameter is not valid
322 255e19d4 Guido Trotter

323 255e19d4 Guido Trotter
    """
324 255e19d4 Guido Trotter
    if nicparams[constants.NIC_MODE] not in constants.NIC_VALID_MODES:
325 255e19d4 Guido Trotter
      err = "Invalid nic mode: %s" % nicparams[constants.NIC_MODE]
326 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
327 255e19d4 Guido Trotter
328 255e19d4 Guido Trotter
    if (nicparams[constants.NIC_MODE] is constants.NIC_MODE_BRIDGED and
329 255e19d4 Guido Trotter
        not nicparams[constants.NIC_LINK]):
330 255e19d4 Guido Trotter
      err = "Missing bridged nic link"
331 255e19d4 Guido Trotter
      raise errors.ConfigurationError(err)
332 255e19d4 Guido Trotter
333 13f1af63 Guido Trotter
  def UpgradeConfig(self):
334 13f1af63 Guido Trotter
    """Fill defaults for missing configuration values.
335 13f1af63 Guido Trotter

336 13f1af63 Guido Trotter
    """
337 13f1af63 Guido Trotter
    if self.nicparams is None:
338 13f1af63 Guido Trotter
      self.nicparams = {}
339 13f1af63 Guido Trotter
      if self.bridge is not None:
340 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_MODE] = constants.NIC_MODE_BRIDGED
341 13f1af63 Guido Trotter
        self.nicparams[constants.NIC_LINK] = self.bridge
342 9b31ca85 Guido Trotter
    # bridge is no longer used it 2.1. The slot is left there to support
343 9b31ca85 Guido Trotter
    # upgrading, but will be removed in 2.2
344 9b31ca85 Guido Trotter
    if self.bridge is not None:
345 9b31ca85 Guido Trotter
      self.bridge = None
346 13f1af63 Guido Trotter
347 a8083063 Iustin Pop
348 a8083063 Iustin Pop
class Disk(ConfigObject):
349 a8083063 Iustin Pop
  """Config object representing a block device."""
350 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
351 08db7c5c Iustin Pop
               "children", "iv_name", "size", "mode"]
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
  def CreateOnSecondary(self):
354 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
355 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
358 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
359 00fb8246 Michael Hanselmann
    return self.dev_type in (constants.LD_DRBD8, constants.LD_LV)
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
  def OpenOnSecondary(self):
362 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
363 fe96220b Iustin Pop
    return self.dev_type in (constants.LD_LV,)
364 a8083063 Iustin Pop
365 222f2dd5 Iustin Pop
  def StaticDevPath(self):
366 222f2dd5 Iustin Pop
    """Return the device path if this device type has a static one.
367 222f2dd5 Iustin Pop

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

372 222f2dd5 Iustin Pop
    """
373 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
374 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
375 222f2dd5 Iustin Pop
    return None
376 222f2dd5 Iustin Pop
377 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
378 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
379 fc1dc9d7 Iustin Pop

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

384 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
385 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
386 fc1dc9d7 Iustin Pop
    -1.
387 fc1dc9d7 Iustin Pop

388 fc1dc9d7 Iustin Pop
    """
389 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
390 fc1dc9d7 Iustin Pop
      return 0
391 fc1dc9d7 Iustin Pop
    return -1
392 fc1dc9d7 Iustin Pop
393 a8083063 Iustin Pop
  def GetNodes(self, node):
394 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
395 a8083063 Iustin Pop

396 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
397 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
398 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
399 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
400 a8083063 Iustin Pop

401 a8083063 Iustin Pop
    """
402 00fb8246 Michael Hanselmann
    if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
403 a8083063 Iustin Pop
      result = [node]
404 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
405 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
406 a8083063 Iustin Pop
      if node not in result:
407 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
408 a8083063 Iustin Pop
    else:
409 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
410 a8083063 Iustin Pop
    return result
411 a8083063 Iustin Pop
412 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
413 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
414 a8083063 Iustin Pop

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

421 a8083063 Iustin Pop
    """
422 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
423 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
424 a8083063 Iustin Pop
    if not self.children:
425 a8083063 Iustin Pop
      # leaf device
426 a8083063 Iustin Pop
      return result
427 a8083063 Iustin Pop
    for node in my_nodes:
428 a8083063 Iustin Pop
      for child in self.children:
429 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
430 a8083063 Iustin Pop
        if len(child_result) == 1:
431 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
432 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
433 a8083063 Iustin Pop
          # own entry for this node describes it completely
434 a8083063 Iustin Pop
          continue
435 a8083063 Iustin Pop
        else:
436 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
437 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
438 a8083063 Iustin Pop
          # one of its descendants
439 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
440 a8083063 Iustin Pop
            if subnode not in my_nodes:
441 a8083063 Iustin Pop
              result.append((subnode, subdisk))
442 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
443 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
444 a8083063 Iustin Pop
            # be different)
445 a8083063 Iustin Pop
    return result
446 a8083063 Iustin Pop
447 acec9d51 Iustin Pop
  def RecordGrow(self, amount):
448 acec9d51 Iustin Pop
    """Update the size of this disk after growth.
449 acec9d51 Iustin Pop

450 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
451 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
452 acec9d51 Iustin Pop
    actual algorithms from bdev.
453 acec9d51 Iustin Pop

454 acec9d51 Iustin Pop
    """
455 acec9d51 Iustin Pop
    if self.dev_type == constants.LD_LV:
456 acec9d51 Iustin Pop
      self.size += amount
457 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
458 acec9d51 Iustin Pop
      if self.children:
459 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
460 acec9d51 Iustin Pop
      self.size += amount
461 acec9d51 Iustin Pop
    else:
462 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
463 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
464 acec9d51 Iustin Pop
465 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
466 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
467 0402302c Iustin Pop

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

470 0402302c Iustin Pop
    The routine descends down and updates its children also, because
471 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
472 0402302c Iustin Pop
    node.
473 0402302c Iustin Pop

474 0402302c Iustin Pop
    Arguments:
475 0402302c Iustin Pop
      - target_node: the node we wish to configure for
476 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
477 0402302c Iustin Pop

478 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
479 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
480 0402302c Iustin Pop
    in the disk tree.
481 0402302c Iustin Pop

482 0402302c Iustin Pop
    """
483 0402302c Iustin Pop
    if self.children:
484 0402302c Iustin Pop
      for child in self.children:
485 0402302c Iustin Pop
        child.SetPhysicalID(target_node, nodes_ip)
486 0402302c Iustin Pop
487 0402302c Iustin Pop
    if self.logical_id is None and self.physical_id is not None:
488 0402302c Iustin Pop
      return
489 0402302c Iustin Pop
    if self.dev_type in constants.LDS_DRBD:
490 f9518d38 Iustin Pop
      pnode, snode, port, pminor, sminor, secret = self.logical_id
491 0402302c Iustin Pop
      if target_node not in (pnode, snode):
492 0402302c Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
493 0402302c Iustin Pop
                                        target_node)
494 0402302c Iustin Pop
      pnode_ip = nodes_ip.get(pnode, None)
495 0402302c Iustin Pop
      snode_ip = nodes_ip.get(snode, None)
496 0402302c Iustin Pop
      if pnode_ip is None or snode_ip is None:
497 0402302c Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
498 0402302c Iustin Pop
                                        " for %s" % str(self))
499 ffa1c0dc Iustin Pop
      p_data = (pnode_ip, port)
500 ffa1c0dc Iustin Pop
      s_data = (snode_ip, port)
501 0402302c Iustin Pop
      if pnode == target_node:
502 f9518d38 Iustin Pop
        self.physical_id = p_data + s_data + (pminor, secret)
503 0402302c Iustin Pop
      else: # it must be secondary, we tested above
504 f9518d38 Iustin Pop
        self.physical_id = s_data + p_data + (sminor, secret)
505 0402302c Iustin Pop
    else:
506 0402302c Iustin Pop
      self.physical_id = self.logical_id
507 0402302c Iustin Pop
    return
508 0402302c Iustin Pop
509 ff9c047c Iustin Pop
  def ToDict(self):
510 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
511 ff9c047c Iustin Pop

512 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
513 ff9c047c Iustin Pop
    standard python types.
514 ff9c047c Iustin Pop

515 ff9c047c Iustin Pop
    """
516 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
517 ff9c047c Iustin Pop
518 ff9c047c Iustin Pop
    for attr in ("children",):
519 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
520 ff9c047c Iustin Pop
      if alist:
521 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
522 ff9c047c Iustin Pop
    return bo
523 ff9c047c Iustin Pop
524 ff9c047c Iustin Pop
  @classmethod
525 ff9c047c Iustin Pop
  def FromDict(cls, val):
526 ff9c047c Iustin Pop
    """Custom function for Disks
527 ff9c047c Iustin Pop

528 ff9c047c Iustin Pop
    """
529 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
530 ff9c047c Iustin Pop
    if obj.children:
531 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
532 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
533 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
534 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
535 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
536 f9518d38 Iustin Pop
    if obj.dev_type in constants.LDS_DRBD:
537 f9518d38 Iustin Pop
      # we need a tuple of length six here
538 f9518d38 Iustin Pop
      if len(obj.logical_id) < 6:
539 f9518d38 Iustin Pop
        obj.logical_id += (None,) * (6 - len(obj.logical_id))
540 ff9c047c Iustin Pop
    return obj
541 ff9c047c Iustin Pop
542 65a15336 Iustin Pop
  def __str__(self):
543 65a15336 Iustin Pop
    """Custom str() formatter for disks.
544 65a15336 Iustin Pop

545 65a15336 Iustin Pop
    """
546 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
547 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
548 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
549 89f28b76 Iustin Pop
      node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
550 00fb8246 Michael Hanselmann
      val = "<DRBD8("
551 073ca59e Iustin Pop
      if self.physical_id is None:
552 073ca59e Iustin Pop
        phy = "unconfigured"
553 073ca59e Iustin Pop
      else:
554 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
555 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
556 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
557 073ca59e Iustin Pop
558 89f28b76 Iustin Pop
      val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
559 89f28b76 Iustin Pop
              (node_a, minor_a, node_b, minor_b, port, phy))
560 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
561 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
562 65a15336 Iustin Pop
      else:
563 65a15336 Iustin Pop
        val += "no local storage"
564 65a15336 Iustin Pop
    else:
565 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
566 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
567 65a15336 Iustin Pop
    if self.iv_name is None:
568 65a15336 Iustin Pop
      val += ", not visible"
569 65a15336 Iustin Pop
    else:
570 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
571 fd965830 Iustin Pop
    if isinstance(self.size, int):
572 fd965830 Iustin Pop
      val += ", size=%dm)>" % self.size
573 fd965830 Iustin Pop
    else:
574 fd965830 Iustin Pop
      val += ", size='%s')>" % (self.size,)
575 65a15336 Iustin Pop
    return val
576 65a15336 Iustin Pop
577 332d0e37 Iustin Pop
  def Verify(self):
578 332d0e37 Iustin Pop
    """Checks that this disk is correctly configured.
579 332d0e37 Iustin Pop

580 332d0e37 Iustin Pop
    """
581 332d0e37 Iustin Pop
    errors = []
582 332d0e37 Iustin Pop
    if self.mode not in constants.DISK_ACCESS_SET:
583 332d0e37 Iustin Pop
      errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
584 332d0e37 Iustin Pop
    return errors
585 332d0e37 Iustin Pop
586 a8083063 Iustin Pop
587 ec29fe40 Iustin Pop
class Instance(TaggableObject):
588 a8083063 Iustin Pop
  """Config object representing an instance."""
589 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
590 a8083063 Iustin Pop
    "name",
591 a8083063 Iustin Pop
    "primary_node",
592 a8083063 Iustin Pop
    "os",
593 e69d05fd Iustin Pop
    "hypervisor",
594 5bf7b5cf Iustin Pop
    "hvparams",
595 5bf7b5cf Iustin Pop
    "beparams",
596 0d68c45d Iustin Pop
    "admin_up",
597 a8083063 Iustin Pop
    "nics",
598 a8083063 Iustin Pop
    "disks",
599 a8083063 Iustin Pop
    "disk_template",
600 58acb49d Alexander Schreiber
    "network_port",
601 be1fa613 Iustin Pop
    "serial_no",
602 a8083063 Iustin Pop
    ]
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
605 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
606 a8083063 Iustin Pop

607 cfcc5c6d Iustin Pop
    This is a simple wrapper over _ComputeAllNodes.
608 cfcc5c6d Iustin Pop

609 cfcc5c6d Iustin Pop
    """
610 cfcc5c6d Iustin Pop
    all_nodes = set(self._ComputeAllNodes())
611 cfcc5c6d Iustin Pop
    all_nodes.discard(self.primary_node)
612 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
613 cfcc5c6d Iustin Pop
614 cfcc5c6d Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
615 cfcc5c6d Iustin Pop
                             "List of secondary nodes")
616 cfcc5c6d Iustin Pop
617 cfcc5c6d Iustin Pop
  def _ComputeAllNodes(self):
618 cfcc5c6d Iustin Pop
    """Compute the list of all nodes.
619 cfcc5c6d Iustin Pop

620 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
621 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
622 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
623 a8083063 Iustin Pop
    dynamically.
624 a8083063 Iustin Pop

625 a8083063 Iustin Pop
    """
626 cfcc5c6d Iustin Pop
    def _Helper(nodes, device):
627 cfcc5c6d Iustin Pop
      """Recursively computes nodes given a top device."""
628 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
629 cfcc5c6d Iustin Pop
        nodea, nodeb = device.logical_id[:2]
630 cfcc5c6d Iustin Pop
        nodes.add(nodea)
631 cfcc5c6d Iustin Pop
        nodes.add(nodeb)
632 a8083063 Iustin Pop
      if device.children:
633 a8083063 Iustin Pop
        for child in device.children:
634 cfcc5c6d Iustin Pop
          _Helper(nodes, child)
635 a8083063 Iustin Pop
636 cfcc5c6d Iustin Pop
    all_nodes = set()
637 99c7b2a1 Iustin Pop
    all_nodes.add(self.primary_node)
638 a8083063 Iustin Pop
    for device in self.disks:
639 cfcc5c6d Iustin Pop
      _Helper(all_nodes, device)
640 cfcc5c6d Iustin Pop
    return tuple(all_nodes)
641 a8083063 Iustin Pop
642 cfcc5c6d Iustin Pop
  all_nodes = property(_ComputeAllNodes, None, None,
643 cfcc5c6d Iustin Pop
                       "List of all nodes of the instance")
644 a8083063 Iustin Pop
645 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
646 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
647 a8083063 Iustin Pop

648 c41eea6e Iustin Pop
    This function figures out what logical volumes should belong on
649 c41eea6e Iustin Pop
    which nodes, recursing through a device tree.
650 a8083063 Iustin Pop

651 c41eea6e Iustin Pop
    @param lvmap: optional dictionary to receive the
652 c41eea6e Iustin Pop
        'node' : ['lv', ...] data.
653 a8083063 Iustin Pop

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

657 a8083063 Iustin Pop
    """
658 a8083063 Iustin Pop
    if node == None:
659 a8083063 Iustin Pop
      node = self.primary_node
660 a8083063 Iustin Pop
661 a8083063 Iustin Pop
    if lvmap is None:
662 a8083063 Iustin Pop
      lvmap = { node : [] }
663 a8083063 Iustin Pop
      ret = lvmap
664 a8083063 Iustin Pop
    else:
665 a8083063 Iustin Pop
      if not node in lvmap:
666 a8083063 Iustin Pop
        lvmap[node] = []
667 a8083063 Iustin Pop
      ret = None
668 a8083063 Iustin Pop
669 a8083063 Iustin Pop
    if not devs:
670 a8083063 Iustin Pop
      devs = self.disks
671 a8083063 Iustin Pop
672 a8083063 Iustin Pop
    for dev in devs:
673 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
674 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
675 a8083063 Iustin Pop
676 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
677 a8083063 Iustin Pop
        if dev.children:
678 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
679 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
680 a8083063 Iustin Pop
681 a8083063 Iustin Pop
      elif dev.children:
682 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
683 a8083063 Iustin Pop
684 a8083063 Iustin Pop
    return ret
685 a8083063 Iustin Pop
686 ad24e046 Iustin Pop
  def FindDisk(self, idx):
687 ad24e046 Iustin Pop
    """Find a disk given having a specified index.
688 644eeef9 Iustin Pop

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

691 ad24e046 Iustin Pop
    @type idx: int
692 ad24e046 Iustin Pop
    @param idx: the disk index
693 ad24e046 Iustin Pop
    @rtype: L{Disk}
694 ad24e046 Iustin Pop
    @return: the corresponding disk
695 ad24e046 Iustin Pop
    @raise errors.OpPrereqError: when the given index is not valid
696 644eeef9 Iustin Pop

697 ad24e046 Iustin Pop
    """
698 ad24e046 Iustin Pop
    try:
699 ad24e046 Iustin Pop
      idx = int(idx)
700 ad24e046 Iustin Pop
      return self.disks[idx]
701 ad24e046 Iustin Pop
    except ValueError, err:
702 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err))
703 ad24e046 Iustin Pop
    except IndexError:
704 ad24e046 Iustin Pop
      raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
705 ad24e046 Iustin Pop
                                 " 0 to %d" % (idx, len(self.disks)))
706 644eeef9 Iustin Pop
707 ff9c047c Iustin Pop
  def ToDict(self):
708 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
709 ff9c047c Iustin Pop

710 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
711 ff9c047c Iustin Pop
    python types.
712 ff9c047c Iustin Pop

713 ff9c047c Iustin Pop
    """
714 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
715 ff9c047c Iustin Pop
716 ff9c047c Iustin Pop
    for attr in "nics", "disks":
717 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
718 ff9c047c Iustin Pop
      if alist:
719 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
720 ff9c047c Iustin Pop
      else:
721 ff9c047c Iustin Pop
        nlist = []
722 ff9c047c Iustin Pop
      bo[attr] = nlist
723 ff9c047c Iustin Pop
    return bo
724 ff9c047c Iustin Pop
725 ff9c047c Iustin Pop
  @classmethod
726 ff9c047c Iustin Pop
  def FromDict(cls, val):
727 ff9c047c Iustin Pop
    """Custom function for instances.
728 ff9c047c Iustin Pop

729 ff9c047c Iustin Pop
    """
730 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
731 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
732 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
733 ff9c047c Iustin Pop
    return obj
734 ff9c047c Iustin Pop
735 a8083063 Iustin Pop
736 a8083063 Iustin Pop
class OS(ConfigObject):
737 a8083063 Iustin Pop
  """Config object representing an operating system."""
738 a8083063 Iustin Pop
  __slots__ = [
739 a8083063 Iustin Pop
    "name",
740 a8083063 Iustin Pop
    "path",
741 37482e7b Guido Trotter
    "status",
742 082a7f91 Guido Trotter
    "api_versions",
743 a8083063 Iustin Pop
    "create_script",
744 a8083063 Iustin Pop
    "export_script",
745 386b57af Iustin Pop
    "import_script",
746 386b57af Iustin Pop
    "rename_script",
747 a8083063 Iustin Pop
    ]
748 a8083063 Iustin Pop
749 d2c807e4 Guido Trotter
  @classmethod
750 d2c807e4 Guido Trotter
  def FromInvalidOS(cls, err):
751 d2c807e4 Guido Trotter
    """Create an OS from an InvalidOS error.
752 d2c807e4 Guido Trotter

753 d2c807e4 Guido Trotter
    This routine knows how to convert an InvalidOS error to an OS
754 d2c807e4 Guido Trotter
    object representing the broken OS with a meaningful error message.
755 d2c807e4 Guido Trotter

756 d2c807e4 Guido Trotter
    """
757 d2c807e4 Guido Trotter
    if not isinstance(err, errors.InvalidOS):
758 d2c807e4 Guido Trotter
      raise errors.ProgrammerError("Trying to initialize an OS from an"
759 d2c807e4 Guido Trotter
                                   " invalid object of type %s" % type(err))
760 d2c807e4 Guido Trotter
761 d2c807e4 Guido Trotter
    return cls(name=err.args[0], path=err.args[1], status=err.args[2])
762 d2c807e4 Guido Trotter
763 37482e7b Guido Trotter
  def __nonzero__(self):
764 37482e7b Guido Trotter
    return self.status == constants.OS_VALID_STATUS
765 37482e7b Guido Trotter
766 37482e7b Guido Trotter
  __bool__ = __nonzero__
767 a8083063 Iustin Pop
768 7c0d6283 Michael Hanselmann
769 ec29fe40 Iustin Pop
class Node(TaggableObject):
770 a8083063 Iustin Pop
  """Config object representing a node."""
771 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
772 ec29fe40 Iustin Pop
    "name",
773 ec29fe40 Iustin Pop
    "primary_ip",
774 ec29fe40 Iustin Pop
    "secondary_ip",
775 be1fa613 Iustin Pop
    "serial_no",
776 8b8b8b81 Iustin Pop
    "master_candidate",
777 fc0fe88c Iustin Pop
    "offline",
778 af64c0ea Iustin Pop
    "drained",
779 ec29fe40 Iustin Pop
    ]
780 a8083063 Iustin Pop
781 a8083063 Iustin Pop
782 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
783 a8083063 Iustin Pop
  """Config object representing the cluster."""
784 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
785 a8083063 Iustin Pop
    "serial_no",
786 a8083063 Iustin Pop
    "rsahostkeypub",
787 a8083063 Iustin Pop
    "highest_used_port",
788 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
789 a8083063 Iustin Pop
    "mac_prefix",
790 a8083063 Iustin Pop
    "volume_group_name",
791 a8083063 Iustin Pop
    "default_bridge",
792 02691904 Alexander Schreiber
    "default_hypervisor",
793 f6bd6e98 Michael Hanselmann
    "master_node",
794 f6bd6e98 Michael Hanselmann
    "master_ip",
795 f6bd6e98 Michael Hanselmann
    "master_netdev",
796 f6bd6e98 Michael Hanselmann
    "cluster_name",
797 f6bd6e98 Michael Hanselmann
    "file_storage_dir",
798 e69d05fd Iustin Pop
    "enabled_hypervisors",
799 5bf7b5cf Iustin Pop
    "hvparams",
800 5bf7b5cf Iustin Pop
    "beparams",
801 c8fcde47 Guido Trotter
    "nicparams",
802 4b7735f9 Iustin Pop
    "candidate_pool_size",
803 b86a6bcd Guido Trotter
    "modify_etc_hosts",
804 a8083063 Iustin Pop
    ]
805 a8083063 Iustin Pop
806 b86a6bcd Guido Trotter
  def UpgradeConfig(self):
807 b86a6bcd Guido Trotter
    """Fill defaults for missing configuration values.
808 b86a6bcd Guido Trotter

809 b86a6bcd Guido Trotter
    """
810 c1b42c18 Guido Trotter
    if self.hvparams is None:
811 c1b42c18 Guido Trotter
      self.hvparams = constants.HVC_DEFAULTS
812 c1b42c18 Guido Trotter
    else:
813 c1b42c18 Guido Trotter
      for hypervisor in self.hvparams:
814 abe609b2 Guido Trotter
        self.hvparams[hypervisor] = FillDict(
815 c1b42c18 Guido Trotter
            constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
816 c1b42c18 Guido Trotter
817 6e34b628 Guido Trotter
    self.beparams = UpgradeGroupedParams(self.beparams,
818 6e34b628 Guido Trotter
                                         constants.BEC_DEFAULTS)
819 c8fcde47 Guido Trotter
    migrate_default_bridge = not self.nicparams
820 c8fcde47 Guido Trotter
    self.nicparams = UpgradeGroupedParams(self.nicparams,
821 c8fcde47 Guido Trotter
                                          constants.NICC_DEFAULTS)
822 c8fcde47 Guido Trotter
    if migrate_default_bridge:
823 c8fcde47 Guido Trotter
      self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
824 c8fcde47 Guido Trotter
        self.default_bridge
825 c1b42c18 Guido Trotter
826 b86a6bcd Guido Trotter
    if self.modify_etc_hosts is None:
827 b86a6bcd Guido Trotter
      self.modify_etc_hosts = True
828 b86a6bcd Guido Trotter
829 9b31ca85 Guido Trotter
    # default_bridge is no longer used it 2.1. The slot is left there to
830 9b31ca85 Guido Trotter
    # support auto-upgrading, but will be removed in 2.2
831 9b31ca85 Guido Trotter
    if self.default_bridge is not None:
832 9b31ca85 Guido Trotter
      self.default_bridge = None
833 9b31ca85 Guido Trotter
834 319856a9 Michael Hanselmann
  def ToDict(self):
835 319856a9 Michael Hanselmann
    """Custom function for cluster.
836 319856a9 Michael Hanselmann

837 319856a9 Michael Hanselmann
    """
838 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
839 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
840 319856a9 Michael Hanselmann
    return mydict
841 319856a9 Michael Hanselmann
842 319856a9 Michael Hanselmann
  @classmethod
843 319856a9 Michael Hanselmann
  def FromDict(cls, val):
844 319856a9 Michael Hanselmann
    """Custom function for cluster.
845 319856a9 Michael Hanselmann

846 319856a9 Michael Hanselmann
    """
847 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
848 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
849 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
850 319856a9 Michael Hanselmann
    return obj
851 319856a9 Michael Hanselmann
852 5bf7b5cf Iustin Pop
  def FillHV(self, instance):
853 5bf7b5cf Iustin Pop
    """Fill an instance's hvparams dict.
854 5bf7b5cf Iustin Pop

855 5bf7b5cf Iustin Pop
    @type instance: object
856 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
857 5bf7b5cf Iustin Pop
    @rtype: dict
858 5bf7b5cf Iustin Pop
    @return: a copy of the instance's hvparams with missing keys filled from
859 5bf7b5cf Iustin Pop
        the cluster defaults
860 5bf7b5cf Iustin Pop

861 5bf7b5cf Iustin Pop
    """
862 abe609b2 Guido Trotter
    return FillDict(self.hvparams.get(instance.hypervisor, {}),
863 5bf7b5cf Iustin Pop
                         instance.hvparams)
864 5bf7b5cf Iustin Pop
865 5bf7b5cf Iustin Pop
  def FillBE(self, instance):
866 5bf7b5cf Iustin Pop
    """Fill an instance's beparams dict.
867 5bf7b5cf Iustin Pop

868 5bf7b5cf Iustin Pop
    @type instance: object
869 5bf7b5cf Iustin Pop
    @param instance: the instance parameter to fill
870 5bf7b5cf Iustin Pop
    @rtype: dict
871 5bf7b5cf Iustin Pop
    @return: a copy of the instance's beparams with missing keys filled from
872 5bf7b5cf Iustin Pop
        the cluster defaults
873 5bf7b5cf Iustin Pop

874 5bf7b5cf Iustin Pop
    """
875 4ef7f423 Guido Trotter
    return FillDict(self.beparams.get(constants.PP_DEFAULT, {}),
876 4ef7f423 Guido Trotter
                          instance.beparams)
877 5bf7b5cf Iustin Pop
878 5c947f38 Iustin Pop
879 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
880 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
881 a8083063 Iustin Pop

882 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
883 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
884 a8083063 Iustin Pop
  buffer.
885 a8083063 Iustin Pop

886 a8083063 Iustin Pop
  """
887 a8083063 Iustin Pop
  def Dumps(self):
888 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
889 a8083063 Iustin Pop
    buf = StringIO()
890 a8083063 Iustin Pop
    self.write(buf)
891 a8083063 Iustin Pop
    return buf.getvalue()
892 a8083063 Iustin Pop
893 a8083063 Iustin Pop
  @staticmethod
894 a8083063 Iustin Pop
  def Loads(data):
895 a8083063 Iustin Pop
    """Load data from a string."""
896 a8083063 Iustin Pop
    buf = StringIO(data)
897 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
898 a8083063 Iustin Pop
    cfp.readfp(buf)
899 a8083063 Iustin Pop
    return cfp