Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 6b90c22e

History | View | Annotate | Download (21.9 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 d5835922 Michael Hanselmann
from cStringIO import StringIO
33 a8083063 Iustin Pop
34 a8083063 Iustin Pop
from ganeti import errors
35 5c947f38 Iustin Pop
from ganeti import constants
36 a8083063 Iustin Pop
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
39 a8083063 Iustin Pop
           "OS", "Node", "Cluster"]
40 a8083063 Iustin Pop
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
class ConfigObject(object):
43 a8083063 Iustin Pop
  """A generic config object.
44 a8083063 Iustin Pop

45 a8083063 Iustin Pop
  It has the following properties:
46 a8083063 Iustin Pop

47 a8083063 Iustin Pop
    - provides somewhat safe recursive unpickling and pickling for its classes
48 a8083063 Iustin Pop
    - unset attributes which are defined in slots are always returned
49 a8083063 Iustin Pop
      as None instead of raising an error
50 a8083063 Iustin Pop

51 a8083063 Iustin Pop
  Classes derived from this must always declare __slots__ (we use many
52 a8083063 Iustin Pop
  config objects and the memory reduction is useful.
53 a8083063 Iustin Pop

54 a8083063 Iustin Pop
  """
55 a8083063 Iustin Pop
  __slots__ = []
56 a8083063 Iustin Pop
57 a8083063 Iustin Pop
  def __init__(self, **kwargs):
58 319856a9 Michael Hanselmann
    for k, v in kwargs.iteritems():
59 319856a9 Michael Hanselmann
      setattr(self, k, v)
60 a8083063 Iustin Pop
61 a8083063 Iustin Pop
  def __getattr__(self, name):
62 a8083063 Iustin Pop
    if name not in self.__slots__:
63 3ecf6786 Iustin Pop
      raise AttributeError("Invalid object attribute %s.%s" %
64 3ecf6786 Iustin Pop
                           (type(self).__name__, name))
65 a8083063 Iustin Pop
    return None
66 a8083063 Iustin Pop
67 47c28c5b Michael Hanselmann
  def __setitem__(self, key, value):
68 47c28c5b Michael Hanselmann
    if key not in self.__slots__:
69 3ecf6786 Iustin Pop
      raise KeyError(key)
70 47c28c5b Michael Hanselmann
    setattr(self, key, value)
71 47c28c5b Michael Hanselmann
72 a8083063 Iustin Pop
  def __getstate__(self):
73 a8083063 Iustin Pop
    state = {}
74 a8083063 Iustin Pop
    for name in self.__slots__:
75 a8083063 Iustin Pop
      if hasattr(self, name):
76 a8083063 Iustin Pop
        state[name] = getattr(self, name)
77 a8083063 Iustin Pop
    return state
78 a8083063 Iustin Pop
79 a8083063 Iustin Pop
  def __setstate__(self, state):
80 a8083063 Iustin Pop
    for name in state:
81 a8083063 Iustin Pop
      if name in self.__slots__:
82 a8083063 Iustin Pop
        setattr(self, name, state[name])
83 a8083063 Iustin Pop
84 ff9c047c Iustin Pop
  def ToDict(self):
85 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
86 ff9c047c Iustin Pop

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

93 ff9c047c Iustin Pop
    """
94 ff9c047c Iustin Pop
    return dict([(k, getattr(self, k, None)) for k in self.__slots__])
95 ff9c047c Iustin Pop
96 ff9c047c Iustin Pop
  @classmethod
97 ff9c047c Iustin Pop
  def FromDict(cls, val):
98 ff9c047c Iustin Pop
    """Create an object from a dictionary.
99 ff9c047c Iustin Pop

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

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

108 ff9c047c Iustin Pop
    """
109 ff9c047c Iustin Pop
    if not isinstance(val, dict):
110 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
111 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
112 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
113 319856a9 Michael Hanselmann
    obj = cls(**val_str)
114 ff9c047c Iustin Pop
    return obj
115 ff9c047c Iustin Pop
116 ff9c047c Iustin Pop
  @staticmethod
117 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
118 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
119 ff9c047c Iustin Pop

120 ff9c047c Iustin Pop
    This method converts a container with elements derived from
121 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
122 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
123 ff9c047c Iustin Pop

124 ff9c047c Iustin Pop
    """
125 ff9c047c Iustin Pop
    if isinstance(container, dict):
126 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
127 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
128 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
129 ff9c047c Iustin Pop
    else:
130 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
131 ff9c047c Iustin Pop
                      type(container))
132 ff9c047c Iustin Pop
    return ret
133 ff9c047c Iustin Pop
134 ff9c047c Iustin Pop
  @staticmethod
135 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
136 ff9c047c Iustin Pop
    """Convert a container from standard python types.
137 ff9c047c Iustin Pop

138 ff9c047c Iustin Pop
    This method converts a container with standard python types to
139 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
140 ff9c047c Iustin Pop
    keys, only the values.
141 ff9c047c Iustin Pop

142 ff9c047c Iustin Pop
    """
143 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
144 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
145 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
146 ff9c047c Iustin Pop
    if c_type is dict:
147 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
148 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
149 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
150 ff9c047c Iustin Pop
    else:
151 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
152 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
153 ff9c047c Iustin Pop
    return ret
154 ff9c047c Iustin Pop
155 ff9c047c Iustin Pop
  def __repr__(self):
156 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
157 ff9c047c Iustin Pop
    return repr(self.ToDict())
158 ff9c047c Iustin Pop
159 a8083063 Iustin Pop
160 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
161 5c947f38 Iustin Pop
  """An generic class supporting tags.
162 5c947f38 Iustin Pop

163 5c947f38 Iustin Pop
  """
164 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
165 2057f6c7 Iustin Pop
166 5c947f38 Iustin Pop
  @staticmethod
167 5c947f38 Iustin Pop
  def ValidateTag(tag):
168 5c947f38 Iustin Pop
    """Check if a tag is valid.
169 5c947f38 Iustin Pop

170 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
171 5c947f38 Iustin Pop
    function has no return value.
172 5c947f38 Iustin Pop

173 5c947f38 Iustin Pop
    """
174 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
175 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
176 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
177 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
178 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
179 5c947f38 Iustin Pop
    if not tag:
180 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
181 5c947f38 Iustin Pop
    if not re.match("^[ \w.+*/:-]+$", tag):
182 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
183 5c947f38 Iustin Pop
184 5c947f38 Iustin Pop
  def GetTags(self):
185 5c947f38 Iustin Pop
    """Return the tags list.
186 5c947f38 Iustin Pop

187 5c947f38 Iustin Pop
    """
188 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
189 5c947f38 Iustin Pop
    if tags is None:
190 5c947f38 Iustin Pop
      tags = self.tags = set()
191 5c947f38 Iustin Pop
    return tags
192 5c947f38 Iustin Pop
193 5c947f38 Iustin Pop
  def AddTag(self, tag):
194 5c947f38 Iustin Pop
    """Add a new tag.
195 5c947f38 Iustin Pop

196 5c947f38 Iustin Pop
    """
197 5c947f38 Iustin Pop
    self.ValidateTag(tag)
198 5c947f38 Iustin Pop
    tags = self.GetTags()
199 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
200 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
201 5c947f38 Iustin Pop
    self.GetTags().add(tag)
202 5c947f38 Iustin Pop
203 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
204 5c947f38 Iustin Pop
    """Remove a tag.
205 5c947f38 Iustin Pop

206 5c947f38 Iustin Pop
    """
207 5c947f38 Iustin Pop
    self.ValidateTag(tag)
208 5c947f38 Iustin Pop
    tags = self.GetTags()
209 5c947f38 Iustin Pop
    try:
210 5c947f38 Iustin Pop
      tags.remove(tag)
211 5c947f38 Iustin Pop
    except KeyError:
212 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
213 5c947f38 Iustin Pop
214 ff9c047c Iustin Pop
  def ToDict(self):
215 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
216 ff9c047c Iustin Pop

217 ff9c047c Iustin Pop
    This replaces the tags set with a list.
218 ff9c047c Iustin Pop

219 ff9c047c Iustin Pop
    """
220 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
221 ff9c047c Iustin Pop
222 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
223 ff9c047c Iustin Pop
    if isinstance(tags, set):
224 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
225 ff9c047c Iustin Pop
    return bo
226 ff9c047c Iustin Pop
227 ff9c047c Iustin Pop
  @classmethod
228 ff9c047c Iustin Pop
  def FromDict(cls, val):
229 ff9c047c Iustin Pop
    """Custom function for instances.
230 ff9c047c Iustin Pop

231 ff9c047c Iustin Pop
    """
232 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
233 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
234 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
235 ff9c047c Iustin Pop
    return obj
236 ff9c047c Iustin Pop
237 5c947f38 Iustin Pop
238 a8083063 Iustin Pop
class ConfigData(ConfigObject):
239 a8083063 Iustin Pop
  """Top-level config object."""
240 b2fddf63 Iustin Pop
  __slots__ = ["cluster", "nodes", "instances"]
241 a8083063 Iustin Pop
242 ff9c047c Iustin Pop
  def ToDict(self):
243 ff9c047c Iustin Pop
    """Custom function for top-level config data.
244 ff9c047c Iustin Pop

245 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
246 ff9c047c Iustin Pop
    with standard python types.
247 ff9c047c Iustin Pop

248 ff9c047c Iustin Pop
    """
249 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
250 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
251 ff9c047c Iustin Pop
    for key in "nodes", "instances":
252 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
253 ff9c047c Iustin Pop
254 ff9c047c Iustin Pop
    return mydict
255 ff9c047c Iustin Pop
256 ff9c047c Iustin Pop
  @classmethod
257 ff9c047c Iustin Pop
  def FromDict(cls, val):
258 ff9c047c Iustin Pop
    """Custom function for top-level config data
259 ff9c047c Iustin Pop

260 ff9c047c Iustin Pop
    """
261 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
262 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
263 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
264 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
265 ff9c047c Iustin Pop
    return obj
266 ff9c047c Iustin Pop
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
class NIC(ConfigObject):
269 a8083063 Iustin Pop
  """Config object representing a network card."""
270 a8083063 Iustin Pop
  __slots__ = ["mac", "ip", "bridge"]
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
273 a8083063 Iustin Pop
class Disk(ConfigObject):
274 a8083063 Iustin Pop
  """Config object representing a block device."""
275 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
276 a8083063 Iustin Pop
               "children", "iv_name", "size"]
277 a8083063 Iustin Pop
278 a8083063 Iustin Pop
  def CreateOnSecondary(self):
279 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
280 a1f445d3 Iustin Pop
    return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8,
281 a1f445d3 Iustin Pop
                             constants.LD_LV)
282 a8083063 Iustin Pop
283 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
284 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
285 a1f445d3 Iustin Pop
    return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8,
286 a1f445d3 Iustin Pop
                             constants.LD_LV)
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
  def OpenOnSecondary(self):
289 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
290 fe96220b Iustin Pop
    return self.dev_type in (constants.LD_LV,)
291 a8083063 Iustin Pop
292 222f2dd5 Iustin Pop
  def StaticDevPath(self):
293 222f2dd5 Iustin Pop
    """Return the device path if this device type has a static one.
294 222f2dd5 Iustin Pop

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

299 222f2dd5 Iustin Pop
    """
300 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
301 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
302 222f2dd5 Iustin Pop
    return None
303 222f2dd5 Iustin Pop
304 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
305 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
306 fc1dc9d7 Iustin Pop

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

311 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
312 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
313 fc1dc9d7 Iustin Pop
    -1.
314 fc1dc9d7 Iustin Pop

315 fc1dc9d7 Iustin Pop
    """
316 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
317 fc1dc9d7 Iustin Pop
      return 0
318 fc1dc9d7 Iustin Pop
    return -1
319 fc1dc9d7 Iustin Pop
320 a8083063 Iustin Pop
  def GetNodes(self, node):
321 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
322 a8083063 Iustin Pop

323 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
324 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
325 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
326 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
327 a8083063 Iustin Pop

328 a8083063 Iustin Pop
    """
329 3f752d97 Manuel Franceschini
    if self.dev_type in [constants.LD_LV, constants.LD_MD_R1,
330 3f752d97 Manuel Franceschini
                         constants.LD_FILE]:
331 a8083063 Iustin Pop
      result = [node]
332 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
333 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
334 a8083063 Iustin Pop
      if node not in result:
335 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
336 a8083063 Iustin Pop
    else:
337 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
338 a8083063 Iustin Pop
    return result
339 a8083063 Iustin Pop
340 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
341 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
342 a8083063 Iustin Pop

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

349 a8083063 Iustin Pop
    """
350 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
351 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
352 a8083063 Iustin Pop
    if not self.children:
353 a8083063 Iustin Pop
      # leaf device
354 a8083063 Iustin Pop
      return result
355 a8083063 Iustin Pop
    for node in my_nodes:
356 a8083063 Iustin Pop
      for child in self.children:
357 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
358 a8083063 Iustin Pop
        if len(child_result) == 1:
359 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
360 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
361 a8083063 Iustin Pop
          # own entry for this node describes it completely
362 a8083063 Iustin Pop
          continue
363 a8083063 Iustin Pop
        else:
364 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
365 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
366 a8083063 Iustin Pop
          # one of its descendants
367 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
368 a8083063 Iustin Pop
            if subnode not in my_nodes:
369 a8083063 Iustin Pop
              result.append((subnode, subdisk))
370 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
371 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
372 a8083063 Iustin Pop
            # be different)
373 a8083063 Iustin Pop
    return result
374 a8083063 Iustin Pop
375 acec9d51 Iustin Pop
  def RecordGrow(self, amount):
376 acec9d51 Iustin Pop
    """Update the size of this disk after growth.
377 acec9d51 Iustin Pop

378 acec9d51 Iustin Pop
    This method recurses over the disks's children and updates their
379 acec9d51 Iustin Pop
    size correspondigly. The method needs to be kept in sync with the
380 acec9d51 Iustin Pop
    actual algorithms from bdev.
381 acec9d51 Iustin Pop

382 acec9d51 Iustin Pop
    """
383 acec9d51 Iustin Pop
    if self.dev_type == constants.LD_LV:
384 acec9d51 Iustin Pop
      self.size += amount
385 acec9d51 Iustin Pop
    elif self.dev_type == constants.LD_DRBD8:
386 acec9d51 Iustin Pop
      if self.children:
387 acec9d51 Iustin Pop
        self.children[0].RecordGrow(amount)
388 acec9d51 Iustin Pop
      self.size += amount
389 acec9d51 Iustin Pop
    else:
390 acec9d51 Iustin Pop
      raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
391 acec9d51 Iustin Pop
                                   " disk type %s" % self.dev_type)
392 acec9d51 Iustin Pop
393 0402302c Iustin Pop
  def SetPhysicalID(self, target_node, nodes_ip):
394 0402302c Iustin Pop
    """Convert the logical ID to the physical ID.
395 0402302c Iustin Pop

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

398 0402302c Iustin Pop
    The routine descends down and updates its children also, because
399 0402302c Iustin Pop
    this helps when the only the top device is passed to the remote
400 0402302c Iustin Pop
    node.
401 0402302c Iustin Pop

402 0402302c Iustin Pop
    Arguments:
403 0402302c Iustin Pop
      - target_node: the node we wish to configure for
404 0402302c Iustin Pop
      - nodes_ip: a mapping of node name to ip
405 0402302c Iustin Pop

406 0402302c Iustin Pop
    The target_node must exist in in nodes_ip, and must be one of the
407 0402302c Iustin Pop
    nodes in the logical ID for each of the DRBD devices encountered
408 0402302c Iustin Pop
    in the disk tree.
409 0402302c Iustin Pop

410 0402302c Iustin Pop
    """
411 0402302c Iustin Pop
    if self.children:
412 0402302c Iustin Pop
      for child in self.children:
413 0402302c Iustin Pop
        child.SetPhysicalID(target_node, nodes_ip)
414 0402302c Iustin Pop
415 0402302c Iustin Pop
    if self.logical_id is None and self.physical_id is not None:
416 0402302c Iustin Pop
      return
417 0402302c Iustin Pop
    if self.dev_type in constants.LDS_DRBD:
418 0402302c Iustin Pop
      pnode, snode, port = self.logical_id
419 0402302c Iustin Pop
      if target_node not in (pnode, snode):
420 0402302c Iustin Pop
        raise errors.ConfigurationError("DRBD device not knowing node %s" %
421 0402302c Iustin Pop
                                        target_node)
422 0402302c Iustin Pop
      pnode_ip = nodes_ip.get(pnode, None)
423 0402302c Iustin Pop
      snode_ip = nodes_ip.get(snode, None)
424 0402302c Iustin Pop
      if pnode_ip is None or snode_ip is None:
425 0402302c Iustin Pop
        raise errors.ConfigurationError("Can't find primary or secondary node"
426 0402302c Iustin Pop
                                        " for %s" % str(self))
427 0402302c Iustin Pop
      if pnode == target_node:
428 0402302c Iustin Pop
        self.physical_id = (pnode_ip, port,
429 0402302c Iustin Pop
                            snode_ip, port)
430 0402302c Iustin Pop
      else: # it must be secondary, we tested above
431 0402302c Iustin Pop
        self.physical_id = (snode_ip, port,
432 0402302c Iustin Pop
                            pnode_ip, port)
433 0402302c Iustin Pop
    else:
434 0402302c Iustin Pop
      self.physical_id = self.logical_id
435 0402302c Iustin Pop
    return
436 0402302c Iustin Pop
437 ff9c047c Iustin Pop
  def ToDict(self):
438 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
439 ff9c047c Iustin Pop

440 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
441 ff9c047c Iustin Pop
    standard python types.
442 ff9c047c Iustin Pop

443 ff9c047c Iustin Pop
    """
444 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
445 ff9c047c Iustin Pop
446 ff9c047c Iustin Pop
    for attr in ("children",):
447 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
448 ff9c047c Iustin Pop
      if alist:
449 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
450 ff9c047c Iustin Pop
    return bo
451 ff9c047c Iustin Pop
452 ff9c047c Iustin Pop
  @classmethod
453 ff9c047c Iustin Pop
  def FromDict(cls, val):
454 ff9c047c Iustin Pop
    """Custom function for Disks
455 ff9c047c Iustin Pop

456 ff9c047c Iustin Pop
    """
457 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
458 ff9c047c Iustin Pop
    if obj.children:
459 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
460 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
461 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
462 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
463 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
464 ff9c047c Iustin Pop
    return obj
465 ff9c047c Iustin Pop
466 65a15336 Iustin Pop
  def __str__(self):
467 65a15336 Iustin Pop
    """Custom str() formatter for disks.
468 65a15336 Iustin Pop

469 65a15336 Iustin Pop
    """
470 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
471 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
472 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
473 65a15336 Iustin Pop
      if self.dev_type == constants.LD_DRBD7:
474 65a15336 Iustin Pop
        val = "<DRBD7("
475 65a15336 Iustin Pop
      else:
476 65a15336 Iustin Pop
        val = "<DRBD8("
477 073ca59e Iustin Pop
      if self.physical_id is None:
478 073ca59e Iustin Pop
        phy = "unconfigured"
479 073ca59e Iustin Pop
      else:
480 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
481 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
482 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
483 073ca59e Iustin Pop
484 073ca59e Iustin Pop
      val += ("hosts=%s-%s, port=%s, %s, " %
485 073ca59e Iustin Pop
              (self.logical_id[0], self.logical_id[1], self.logical_id[2],
486 073ca59e Iustin Pop
               phy))
487 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
488 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
489 65a15336 Iustin Pop
      else:
490 65a15336 Iustin Pop
        val += "no local storage"
491 65a15336 Iustin Pop
    elif self.dev_type == constants.LD_MD_R1:
492 65a15336 Iustin Pop
      val = "<MD_R1(uuid=%s, children=%s" % (self.physical_id, self.children)
493 65a15336 Iustin Pop
    else:
494 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
495 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
496 65a15336 Iustin Pop
    if self.iv_name is None:
497 65a15336 Iustin Pop
      val += ", not visible"
498 65a15336 Iustin Pop
    else:
499 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
500 65a15336 Iustin Pop
    val += ", size=%dm)>" % self.size
501 65a15336 Iustin Pop
    return val
502 65a15336 Iustin Pop
503 a8083063 Iustin Pop
504 ec29fe40 Iustin Pop
class Instance(TaggableObject):
505 a8083063 Iustin Pop
  """Config object representing an instance."""
506 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
507 a8083063 Iustin Pop
    "name",
508 a8083063 Iustin Pop
    "primary_node",
509 a8083063 Iustin Pop
    "os",
510 a8083063 Iustin Pop
    "status",
511 a8083063 Iustin Pop
    "memory",
512 a8083063 Iustin Pop
    "vcpus",
513 a8083063 Iustin Pop
    "nics",
514 a8083063 Iustin Pop
    "disks",
515 a8083063 Iustin Pop
    "disk_template",
516 58acb49d Alexander Schreiber
    "network_port",
517 2d8c9c2e Iustin Pop
    "kernel_path",
518 2d8c9c2e Iustin Pop
    "initrd_path",
519 0d9c53bc Alexander Schreiber
    "hvm_boot_order",
520 ddd9bc04 Iustin Pop
    "hvm_acpi",
521 ddd9bc04 Iustin Pop
    "hvm_pae",
522 ddd9bc04 Iustin Pop
    "hvm_cdrom_image_path",
523 ddd9bc04 Iustin Pop
    "vnc_bind_address",
524 a8083063 Iustin Pop
    ]
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
527 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
528 a8083063 Iustin Pop

529 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
530 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
531 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
532 a8083063 Iustin Pop
    dynamically.
533 a8083063 Iustin Pop

534 a8083063 Iustin Pop
    """
535 a8083063 Iustin Pop
    def _Helper(primary, sec_nodes, device):
536 a8083063 Iustin Pop
      """Recursively computes secondary nodes given a top device."""
537 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
538 a8083063 Iustin Pop
        nodea, nodeb, dummy = device.logical_id
539 a8083063 Iustin Pop
        if nodea == primary:
540 a8083063 Iustin Pop
          candidate = nodeb
541 a8083063 Iustin Pop
        else:
542 a8083063 Iustin Pop
          candidate = nodea
543 a8083063 Iustin Pop
        if candidate not in sec_nodes:
544 a8083063 Iustin Pop
          sec_nodes.append(candidate)
545 a8083063 Iustin Pop
      if device.children:
546 a8083063 Iustin Pop
        for child in device.children:
547 a8083063 Iustin Pop
          _Helper(primary, sec_nodes, child)
548 a8083063 Iustin Pop
549 a8083063 Iustin Pop
    secondary_nodes = []
550 a8083063 Iustin Pop
    for device in self.disks:
551 a8083063 Iustin Pop
      _Helper(self.primary_node, secondary_nodes, device)
552 a8083063 Iustin Pop
    return tuple(secondary_nodes)
553 a8083063 Iustin Pop
554 a8083063 Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
555 a8083063 Iustin Pop
                             "List of secondary nodes")
556 a8083063 Iustin Pop
557 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
558 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
559 a8083063 Iustin Pop

560 a8083063 Iustin Pop
    This function figures out what logical volumes should belong on which
561 a8083063 Iustin Pop
    nodes, recursing through a device tree.
562 a8083063 Iustin Pop

563 a8083063 Iustin Pop
    Args:
564 a8083063 Iustin Pop
      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
565 a8083063 Iustin Pop

566 a8083063 Iustin Pop
    Returns:
567 a8083063 Iustin Pop
      None if lvmap arg is given.
568 a8083063 Iustin Pop
      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
569 a8083063 Iustin Pop

570 a8083063 Iustin Pop
    """
571 a8083063 Iustin Pop
    if node == None:
572 a8083063 Iustin Pop
      node = self.primary_node
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
    if lvmap is None:
575 a8083063 Iustin Pop
      lvmap = { node : [] }
576 a8083063 Iustin Pop
      ret = lvmap
577 a8083063 Iustin Pop
    else:
578 a8083063 Iustin Pop
      if not node in lvmap:
579 a8083063 Iustin Pop
        lvmap[node] = []
580 a8083063 Iustin Pop
      ret = None
581 a8083063 Iustin Pop
582 a8083063 Iustin Pop
    if not devs:
583 a8083063 Iustin Pop
      devs = self.disks
584 a8083063 Iustin Pop
585 a8083063 Iustin Pop
    for dev in devs:
586 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
587 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
588 a8083063 Iustin Pop
589 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
590 a8083063 Iustin Pop
        if dev.logical_id[0] not in lvmap:
591 a8083063 Iustin Pop
          lvmap[dev.logical_id[0]] = []
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
        if dev.logical_id[1] not in lvmap:
594 a8083063 Iustin Pop
          lvmap[dev.logical_id[1]] = []
595 a8083063 Iustin Pop
596 a8083063 Iustin Pop
        if dev.children:
597 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
598 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
599 a8083063 Iustin Pop
600 a8083063 Iustin Pop
      elif dev.children:
601 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
602 a8083063 Iustin Pop
603 a8083063 Iustin Pop
    return ret
604 a8083063 Iustin Pop
605 644eeef9 Iustin Pop
  def FindDisk(self, name):
606 644eeef9 Iustin Pop
    """Find a disk given having a specified name.
607 644eeef9 Iustin Pop

608 644eeef9 Iustin Pop
    This will return the disk which has the given iv_name.
609 644eeef9 Iustin Pop

610 644eeef9 Iustin Pop
    """
611 644eeef9 Iustin Pop
    for disk in self.disks:
612 644eeef9 Iustin Pop
      if disk.iv_name == name:
613 644eeef9 Iustin Pop
        return disk
614 644eeef9 Iustin Pop
615 644eeef9 Iustin Pop
    return None
616 644eeef9 Iustin Pop
617 ff9c047c Iustin Pop
  def ToDict(self):
618 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
619 ff9c047c Iustin Pop

620 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
621 ff9c047c Iustin Pop
    python types.
622 ff9c047c Iustin Pop

623 ff9c047c Iustin Pop
    """
624 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
625 ff9c047c Iustin Pop
626 ff9c047c Iustin Pop
    for attr in "nics", "disks":
627 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
628 ff9c047c Iustin Pop
      if alist:
629 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
630 ff9c047c Iustin Pop
      else:
631 ff9c047c Iustin Pop
        nlist = []
632 ff9c047c Iustin Pop
      bo[attr] = nlist
633 ff9c047c Iustin Pop
    return bo
634 ff9c047c Iustin Pop
635 ff9c047c Iustin Pop
  @classmethod
636 ff9c047c Iustin Pop
  def FromDict(cls, val):
637 ff9c047c Iustin Pop
    """Custom function for instances.
638 ff9c047c Iustin Pop

639 ff9c047c Iustin Pop
    """
640 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
641 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
642 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
643 ff9c047c Iustin Pop
    return obj
644 ff9c047c Iustin Pop
645 a8083063 Iustin Pop
646 a8083063 Iustin Pop
class OS(ConfigObject):
647 a8083063 Iustin Pop
  """Config object representing an operating system."""
648 a8083063 Iustin Pop
  __slots__ = [
649 a8083063 Iustin Pop
    "name",
650 a8083063 Iustin Pop
    "path",
651 37482e7b Guido Trotter
    "status",
652 a8083063 Iustin Pop
    "api_version",
653 a8083063 Iustin Pop
    "create_script",
654 a8083063 Iustin Pop
    "export_script",
655 386b57af Iustin Pop
    "import_script",
656 386b57af Iustin Pop
    "rename_script",
657 a8083063 Iustin Pop
    ]
658 a8083063 Iustin Pop
659 d2c807e4 Guido Trotter
  @classmethod
660 d2c807e4 Guido Trotter
  def FromInvalidOS(cls, err):
661 d2c807e4 Guido Trotter
    """Create an OS from an InvalidOS error.
662 d2c807e4 Guido Trotter

663 d2c807e4 Guido Trotter
    This routine knows how to convert an InvalidOS error to an OS
664 d2c807e4 Guido Trotter
    object representing the broken OS with a meaningful error message.
665 d2c807e4 Guido Trotter

666 d2c807e4 Guido Trotter
    """
667 d2c807e4 Guido Trotter
    if not isinstance(err, errors.InvalidOS):
668 d2c807e4 Guido Trotter
      raise errors.ProgrammerError("Trying to initialize an OS from an"
669 d2c807e4 Guido Trotter
                                   " invalid object of type %s" % type(err))
670 d2c807e4 Guido Trotter
671 d2c807e4 Guido Trotter
    return cls(name=err.args[0], path=err.args[1], status=err.args[2])
672 d2c807e4 Guido Trotter
673 37482e7b Guido Trotter
  def __nonzero__(self):
674 37482e7b Guido Trotter
    return self.status == constants.OS_VALID_STATUS
675 37482e7b Guido Trotter
676 37482e7b Guido Trotter
  __bool__ = __nonzero__
677 a8083063 Iustin Pop
678 7c0d6283 Michael Hanselmann
679 ec29fe40 Iustin Pop
class Node(TaggableObject):
680 a8083063 Iustin Pop
  """Config object representing a node."""
681 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
682 ec29fe40 Iustin Pop
    "name",
683 ec29fe40 Iustin Pop
    "primary_ip",
684 ec29fe40 Iustin Pop
    "secondary_ip",
685 ec29fe40 Iustin Pop
    ]
686 a8083063 Iustin Pop
687 a8083063 Iustin Pop
688 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
689 a8083063 Iustin Pop
  """Config object representing the cluster."""
690 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
691 a8083063 Iustin Pop
    "config_version",
692 a8083063 Iustin Pop
    "serial_no",
693 a8083063 Iustin Pop
    "rsahostkeypub",
694 a8083063 Iustin Pop
    "highest_used_port",
695 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
696 a8083063 Iustin Pop
    "mac_prefix",
697 a8083063 Iustin Pop
    "volume_group_name",
698 a8083063 Iustin Pop
    "default_bridge",
699 a8083063 Iustin Pop
    ]
700 a8083063 Iustin Pop
701 319856a9 Michael Hanselmann
  def ToDict(self):
702 319856a9 Michael Hanselmann
    """Custom function for cluster.
703 319856a9 Michael Hanselmann

704 319856a9 Michael Hanselmann
    """
705 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
706 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
707 319856a9 Michael Hanselmann
    return mydict
708 319856a9 Michael Hanselmann
709 319856a9 Michael Hanselmann
  @classmethod
710 319856a9 Michael Hanselmann
  def FromDict(cls, val):
711 319856a9 Michael Hanselmann
    """Custom function for cluster.
712 319856a9 Michael Hanselmann

713 319856a9 Michael Hanselmann
    """
714 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
715 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
716 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
717 319856a9 Michael Hanselmann
    return obj
718 319856a9 Michael Hanselmann
719 5c947f38 Iustin Pop
720 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
721 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
722 a8083063 Iustin Pop

723 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
724 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
725 a8083063 Iustin Pop
  buffer.
726 a8083063 Iustin Pop

727 a8083063 Iustin Pop
  """
728 a8083063 Iustin Pop
  def Dumps(self):
729 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
730 a8083063 Iustin Pop
    buf = StringIO()
731 a8083063 Iustin Pop
    self.write(buf)
732 a8083063 Iustin Pop
    return buf.getvalue()
733 a8083063 Iustin Pop
734 a8083063 Iustin Pop
  @staticmethod
735 a8083063 Iustin Pop
  def Loads(data):
736 a8083063 Iustin Pop
    """Load data from a string."""
737 a8083063 Iustin Pop
    buf = StringIO(data)
738 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
739 a8083063 Iustin Pop
    cfp.readfp(buf)
740 a8083063 Iustin Pop
    return cfp