Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 9ff7e35c

History | View | Annotate | Download (17.2 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
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 319856a9 Michael Hanselmann
import simplejson
31 a8083063 Iustin Pop
from cStringIO import StringIO
32 a8083063 Iustin Pop
import ConfigParser
33 5c947f38 Iustin Pop
import re
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 a8083063 Iustin Pop
           "OS", "Node", "Cluster"]
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
43 a8083063 Iustin Pop
class ConfigObject(object):
44 a8083063 Iustin Pop
  """A generic config object.
45 a8083063 Iustin Pop

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

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

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

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

88 a8083063 Iustin Pop
    """
89 319856a9 Michael Hanselmann
    simplejson.dump(self.ToDict(), fobj)
90 a8083063 Iustin Pop
91 319856a9 Michael Hanselmann
  @classmethod
92 319856a9 Michael Hanselmann
  def Load(cls, fobj):
93 319856a9 Michael Hanselmann
    """Load data from the given stream.
94 a8083063 Iustin Pop

95 a8083063 Iustin Pop
    """
96 319856a9 Michael Hanselmann
    return cls.FromDict(simplejson.load(fobj))
97 a8083063 Iustin Pop
98 a8083063 Iustin Pop
  def Dumps(self):
99 319856a9 Michael Hanselmann
    """Dump and return the string representation."""
100 a8083063 Iustin Pop
    buf = StringIO()
101 a8083063 Iustin Pop
    self.Dump(buf)
102 a8083063 Iustin Pop
    return buf.getvalue()
103 a8083063 Iustin Pop
104 319856a9 Michael Hanselmann
  @classmethod
105 319856a9 Michael Hanselmann
  def Loads(cls, data):
106 a8083063 Iustin Pop
    """Load data from a string."""
107 319856a9 Michael Hanselmann
    return cls.Load(StringIO(data))
108 a8083063 Iustin Pop
109 ff9c047c Iustin Pop
  def ToDict(self):
110 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
111 ff9c047c Iustin Pop

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

118 ff9c047c Iustin Pop
    """
119 ff9c047c Iustin Pop
    return dict([(k, getattr(self, k, None)) for k in self.__slots__])
120 ff9c047c Iustin Pop
121 ff9c047c Iustin Pop
  @classmethod
122 ff9c047c Iustin Pop
  def FromDict(cls, val):
123 ff9c047c Iustin Pop
    """Create an object from a dictionary.
124 ff9c047c Iustin Pop

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

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

133 ff9c047c Iustin Pop
    """
134 ff9c047c Iustin Pop
    if not isinstance(val, dict):
135 ff9c047c Iustin Pop
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
136 ff9c047c Iustin Pop
                                      " expected dict, got %s" % type(val))
137 319856a9 Michael Hanselmann
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
138 319856a9 Michael Hanselmann
    obj = cls(**val_str)
139 ff9c047c Iustin Pop
    return obj
140 ff9c047c Iustin Pop
141 ff9c047c Iustin Pop
  @staticmethod
142 ff9c047c Iustin Pop
  def _ContainerToDicts(container):
143 ff9c047c Iustin Pop
    """Convert the elements of a container to standard python types.
144 ff9c047c Iustin Pop

145 ff9c047c Iustin Pop
    This method converts a container with elements derived from
146 ff9c047c Iustin Pop
    ConfigData to standard python types. If the container is a dict,
147 ff9c047c Iustin Pop
    we don't touch the keys, only the values.
148 ff9c047c Iustin Pop

149 ff9c047c Iustin Pop
    """
150 ff9c047c Iustin Pop
    if isinstance(container, dict):
151 ff9c047c Iustin Pop
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
152 ff9c047c Iustin Pop
    elif isinstance(container, (list, tuple, set, frozenset)):
153 ff9c047c Iustin Pop
      ret = [elem.ToDict() for elem in container]
154 ff9c047c Iustin Pop
    else:
155 ff9c047c Iustin Pop
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
156 ff9c047c Iustin Pop
                      type(container))
157 ff9c047c Iustin Pop
    return ret
158 ff9c047c Iustin Pop
159 ff9c047c Iustin Pop
  @staticmethod
160 ff9c047c Iustin Pop
  def _ContainerFromDicts(source, c_type, e_type):
161 ff9c047c Iustin Pop
    """Convert a container from standard python types.
162 ff9c047c Iustin Pop

163 ff9c047c Iustin Pop
    This method converts a container with standard python types to
164 ff9c047c Iustin Pop
    ConfigData objects. If the container is a dict, we don't touch the
165 ff9c047c Iustin Pop
    keys, only the values.
166 ff9c047c Iustin Pop

167 ff9c047c Iustin Pop
    """
168 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
169 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
170 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
171 ff9c047c Iustin Pop
    if c_type is dict:
172 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
173 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
174 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
175 ff9c047c Iustin Pop
    else:
176 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
177 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
178 ff9c047c Iustin Pop
    return ret
179 ff9c047c Iustin Pop
180 ff9c047c Iustin Pop
  def __repr__(self):
181 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
182 ff9c047c Iustin Pop
    return repr(self.ToDict())
183 ff9c047c Iustin Pop
184 a8083063 Iustin Pop
185 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
186 5c947f38 Iustin Pop
  """An generic class supporting tags.
187 5c947f38 Iustin Pop

188 5c947f38 Iustin Pop
  """
189 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
190 2057f6c7 Iustin Pop
191 5c947f38 Iustin Pop
  @staticmethod
192 5c947f38 Iustin Pop
  def ValidateTag(tag):
193 5c947f38 Iustin Pop
    """Check if a tag is valid.
194 5c947f38 Iustin Pop

195 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
196 5c947f38 Iustin Pop
    function has no return value.
197 5c947f38 Iustin Pop

198 5c947f38 Iustin Pop
    """
199 5c947f38 Iustin Pop
    if not isinstance(tag, basestring):
200 3ecf6786 Iustin Pop
      raise errors.TagError("Invalid tag type (not a string)")
201 5c947f38 Iustin Pop
    if len(tag) > constants.MAX_TAG_LEN:
202 319856a9 Michael Hanselmann
      raise errors.TagError("Tag too long (>%d characters)" %
203 319856a9 Michael Hanselmann
                            constants.MAX_TAG_LEN)
204 5c947f38 Iustin Pop
    if not tag:
205 3ecf6786 Iustin Pop
      raise errors.TagError("Tags cannot be empty")
206 5c947f38 Iustin Pop
    if not re.match("^[ \w.+*/:-]+$", tag):
207 3ecf6786 Iustin Pop
      raise errors.TagError("Tag contains invalid characters")
208 5c947f38 Iustin Pop
209 5c947f38 Iustin Pop
  def GetTags(self):
210 5c947f38 Iustin Pop
    """Return the tags list.
211 5c947f38 Iustin Pop

212 5c947f38 Iustin Pop
    """
213 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
214 5c947f38 Iustin Pop
    if tags is None:
215 5c947f38 Iustin Pop
      tags = self.tags = set()
216 5c947f38 Iustin Pop
    return tags
217 5c947f38 Iustin Pop
218 5c947f38 Iustin Pop
  def AddTag(self, tag):
219 5c947f38 Iustin Pop
    """Add a new tag.
220 5c947f38 Iustin Pop

221 5c947f38 Iustin Pop
    """
222 5c947f38 Iustin Pop
    self.ValidateTag(tag)
223 5c947f38 Iustin Pop
    tags = self.GetTags()
224 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
225 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
226 5c947f38 Iustin Pop
    self.GetTags().add(tag)
227 5c947f38 Iustin Pop
228 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
229 5c947f38 Iustin Pop
    """Remove a tag.
230 5c947f38 Iustin Pop

231 5c947f38 Iustin Pop
    """
232 5c947f38 Iustin Pop
    self.ValidateTag(tag)
233 5c947f38 Iustin Pop
    tags = self.GetTags()
234 5c947f38 Iustin Pop
    try:
235 5c947f38 Iustin Pop
      tags.remove(tag)
236 5c947f38 Iustin Pop
    except KeyError:
237 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
238 5c947f38 Iustin Pop
239 ff9c047c Iustin Pop
  def ToDict(self):
240 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
241 ff9c047c Iustin Pop

242 ff9c047c Iustin Pop
    This replaces the tags set with a list.
243 ff9c047c Iustin Pop

244 ff9c047c Iustin Pop
    """
245 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
246 ff9c047c Iustin Pop
247 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
248 ff9c047c Iustin Pop
    if isinstance(tags, set):
249 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
250 ff9c047c Iustin Pop
    return bo
251 ff9c047c Iustin Pop
252 ff9c047c Iustin Pop
  @classmethod
253 ff9c047c Iustin Pop
  def FromDict(cls, val):
254 ff9c047c Iustin Pop
    """Custom function for instances.
255 ff9c047c Iustin Pop

256 ff9c047c Iustin Pop
    """
257 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
258 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
259 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
260 ff9c047c Iustin Pop
    return obj
261 ff9c047c Iustin Pop
262 5c947f38 Iustin Pop
263 a8083063 Iustin Pop
class ConfigData(ConfigObject):
264 a8083063 Iustin Pop
  """Top-level config object."""
265 b2fddf63 Iustin Pop
  __slots__ = ["cluster", "nodes", "instances"]
266 a8083063 Iustin Pop
267 ff9c047c Iustin Pop
  def ToDict(self):
268 ff9c047c Iustin Pop
    """Custom function for top-level config data.
269 ff9c047c Iustin Pop

270 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
271 ff9c047c Iustin Pop
    with standard python types.
272 ff9c047c Iustin Pop

273 ff9c047c Iustin Pop
    """
274 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
275 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
276 ff9c047c Iustin Pop
    for key in "nodes", "instances":
277 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
278 ff9c047c Iustin Pop
279 ff9c047c Iustin Pop
    return mydict
280 ff9c047c Iustin Pop
281 ff9c047c Iustin Pop
  @classmethod
282 ff9c047c Iustin Pop
  def FromDict(cls, val):
283 ff9c047c Iustin Pop
    """Custom function for top-level config data
284 ff9c047c Iustin Pop

285 ff9c047c Iustin Pop
    """
286 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
287 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
288 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
289 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
290 ff9c047c Iustin Pop
    return obj
291 ff9c047c Iustin Pop
292 a8083063 Iustin Pop
293 a8083063 Iustin Pop
class NIC(ConfigObject):
294 a8083063 Iustin Pop
  """Config object representing a network card."""
295 a8083063 Iustin Pop
  __slots__ = ["mac", "ip", "bridge"]
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
class Disk(ConfigObject):
299 a8083063 Iustin Pop
  """Config object representing a block device."""
300 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
301 a8083063 Iustin Pop
               "children", "iv_name", "size"]
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
  def CreateOnSecondary(self):
304 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
305 a8083063 Iustin Pop
    return self.dev_type in ("drbd", "lvm")
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
308 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
309 a8083063 Iustin Pop
    return self.dev_type in ("drbd", "lvm")
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
  def OpenOnSecondary(self):
312 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
313 a8083063 Iustin Pop
    return self.dev_type in ("lvm",)
314 a8083063 Iustin Pop
315 a8083063 Iustin Pop
  def GetNodes(self, node):
316 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
317 a8083063 Iustin Pop

318 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
319 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
320 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
321 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
322 a8083063 Iustin Pop

323 a8083063 Iustin Pop
    """
324 a8083063 Iustin Pop
    if self.dev_type == "lvm" or self.dev_type == "md_raid1":
325 a8083063 Iustin Pop
      result = [node]
326 a8083063 Iustin Pop
    elif self.dev_type == "drbd":
327 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
328 a8083063 Iustin Pop
      if node not in result:
329 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
330 a8083063 Iustin Pop
    else:
331 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
332 a8083063 Iustin Pop
    return result
333 a8083063 Iustin Pop
334 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
335 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
336 a8083063 Iustin Pop

337 a8083063 Iustin Pop
    This method, given the node on which the parent disk lives, will
338 a8083063 Iustin Pop
    return the list of all (node, disk) pairs which describe the disk
339 a8083063 Iustin Pop
    tree in the most compact way. For example, a md/drbd/lvm stack
340 a8083063 Iustin Pop
    will be returned as (primary_node, md) and (secondary_node, drbd)
341 a8083063 Iustin Pop
    which represents all the top-level devices on the nodes. This
342 a8083063 Iustin Pop
    means that on the primary node we need to activate the the md (and
343 a8083063 Iustin Pop
    recursively all its children) and on the secondary node we need to
344 a8083063 Iustin Pop
    activate the drbd device (and its children, the two lvm volumes).
345 a8083063 Iustin Pop

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

375 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
376 ff9c047c Iustin Pop
    standard python types.
377 ff9c047c Iustin Pop

378 ff9c047c Iustin Pop
    """
379 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
380 ff9c047c Iustin Pop
381 ff9c047c Iustin Pop
    for attr in ("children",):
382 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
383 ff9c047c Iustin Pop
      if alist:
384 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
385 ff9c047c Iustin Pop
    return bo
386 ff9c047c Iustin Pop
387 ff9c047c Iustin Pop
  @classmethod
388 ff9c047c Iustin Pop
  def FromDict(cls, val):
389 ff9c047c Iustin Pop
    """Custom function for Disks
390 ff9c047c Iustin Pop

391 ff9c047c Iustin Pop
    """
392 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
393 ff9c047c Iustin Pop
    if obj.children:
394 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
395 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
396 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
397 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
398 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
399 ff9c047c Iustin Pop
    return obj
400 ff9c047c Iustin Pop
401 a8083063 Iustin Pop
402 ec29fe40 Iustin Pop
class Instance(TaggableObject):
403 a8083063 Iustin Pop
  """Config object representing an instance."""
404 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
405 a8083063 Iustin Pop
    "name",
406 a8083063 Iustin Pop
    "primary_node",
407 a8083063 Iustin Pop
    "os",
408 a8083063 Iustin Pop
    "status",
409 a8083063 Iustin Pop
    "memory",
410 a8083063 Iustin Pop
    "vcpus",
411 a8083063 Iustin Pop
    "nics",
412 a8083063 Iustin Pop
    "disks",
413 a8083063 Iustin Pop
    "disk_template",
414 a8083063 Iustin Pop
    ]
415 a8083063 Iustin Pop
416 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
417 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
418 a8083063 Iustin Pop

419 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
420 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
421 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
422 a8083063 Iustin Pop
    dynamically.
423 a8083063 Iustin Pop

424 a8083063 Iustin Pop
    """
425 a8083063 Iustin Pop
    def _Helper(primary, sec_nodes, device):
426 a8083063 Iustin Pop
      """Recursively computes secondary nodes given a top device."""
427 a8083063 Iustin Pop
      if device.dev_type == 'drbd':
428 a8083063 Iustin Pop
        nodea, nodeb, dummy = device.logical_id
429 a8083063 Iustin Pop
        if nodea == primary:
430 a8083063 Iustin Pop
          candidate = nodeb
431 a8083063 Iustin Pop
        else:
432 a8083063 Iustin Pop
          candidate = nodea
433 a8083063 Iustin Pop
        if candidate not in sec_nodes:
434 a8083063 Iustin Pop
          sec_nodes.append(candidate)
435 a8083063 Iustin Pop
      if device.children:
436 a8083063 Iustin Pop
        for child in device.children:
437 a8083063 Iustin Pop
          _Helper(primary, sec_nodes, child)
438 a8083063 Iustin Pop
439 a8083063 Iustin Pop
    secondary_nodes = []
440 a8083063 Iustin Pop
    for device in self.disks:
441 a8083063 Iustin Pop
      _Helper(self.primary_node, secondary_nodes, device)
442 a8083063 Iustin Pop
    return tuple(secondary_nodes)
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
445 a8083063 Iustin Pop
                             "List of secondary nodes")
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
448 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
449 a8083063 Iustin Pop

450 a8083063 Iustin Pop
    This function figures out what logical volumes should belong on which
451 a8083063 Iustin Pop
    nodes, recursing through a device tree.
452 a8083063 Iustin Pop

453 a8083063 Iustin Pop
    Args:
454 a8083063 Iustin Pop
      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
455 a8083063 Iustin Pop

456 a8083063 Iustin Pop
    Returns:
457 a8083063 Iustin Pop
      None if lvmap arg is given.
458 a8083063 Iustin Pop
      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
459 a8083063 Iustin Pop

460 a8083063 Iustin Pop
    """
461 a8083063 Iustin Pop
    if node == None:
462 a8083063 Iustin Pop
      node = self.primary_node
463 a8083063 Iustin Pop
464 a8083063 Iustin Pop
    if lvmap is None:
465 a8083063 Iustin Pop
      lvmap = { node : [] }
466 a8083063 Iustin Pop
      ret = lvmap
467 a8083063 Iustin Pop
    else:
468 a8083063 Iustin Pop
      if not node in lvmap:
469 a8083063 Iustin Pop
        lvmap[node] = []
470 a8083063 Iustin Pop
      ret = None
471 a8083063 Iustin Pop
472 a8083063 Iustin Pop
    if not devs:
473 a8083063 Iustin Pop
      devs = self.disks
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
    for dev in devs:
476 a8083063 Iustin Pop
      if dev.dev_type == "lvm":
477 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
478 a8083063 Iustin Pop
479 a8083063 Iustin Pop
      elif dev.dev_type == "drbd":
480 a8083063 Iustin Pop
        if dev.logical_id[0] not in lvmap:
481 a8083063 Iustin Pop
          lvmap[dev.logical_id[0]] = []
482 a8083063 Iustin Pop
483 a8083063 Iustin Pop
        if dev.logical_id[1] not in lvmap:
484 a8083063 Iustin Pop
          lvmap[dev.logical_id[1]] = []
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
        if dev.children:
487 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
488 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
      elif dev.children:
491 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
492 a8083063 Iustin Pop
493 a8083063 Iustin Pop
    return ret
494 a8083063 Iustin Pop
495 644eeef9 Iustin Pop
  def FindDisk(self, name):
496 644eeef9 Iustin Pop
    """Find a disk given having a specified name.
497 644eeef9 Iustin Pop

498 644eeef9 Iustin Pop
    This will return the disk which has the given iv_name.
499 644eeef9 Iustin Pop

500 644eeef9 Iustin Pop
    """
501 644eeef9 Iustin Pop
    for disk in self.disks:
502 644eeef9 Iustin Pop
      if disk.iv_name == name:
503 644eeef9 Iustin Pop
        return disk
504 644eeef9 Iustin Pop
505 644eeef9 Iustin Pop
    return None
506 644eeef9 Iustin Pop
507 ff9c047c Iustin Pop
  def ToDict(self):
508 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
509 ff9c047c Iustin Pop

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

513 ff9c047c Iustin Pop
    """
514 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
515 ff9c047c Iustin Pop
516 ff9c047c Iustin Pop
    for attr in "nics", "disks":
517 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
518 ff9c047c Iustin Pop
      if alist:
519 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
520 ff9c047c Iustin Pop
      else:
521 ff9c047c Iustin Pop
        nlist = []
522 ff9c047c Iustin Pop
      bo[attr] = nlist
523 ff9c047c Iustin Pop
    return bo
524 ff9c047c Iustin Pop
525 ff9c047c Iustin Pop
  @classmethod
526 ff9c047c Iustin Pop
  def FromDict(cls, val):
527 ff9c047c Iustin Pop
    """Custom function for instances.
528 ff9c047c Iustin Pop

529 ff9c047c Iustin Pop
    """
530 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
531 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
532 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
533 ff9c047c Iustin Pop
    return obj
534 ff9c047c Iustin Pop
535 a8083063 Iustin Pop
536 a8083063 Iustin Pop
class OS(ConfigObject):
537 a8083063 Iustin Pop
  """Config object representing an operating system."""
538 a8083063 Iustin Pop
  __slots__ = [
539 a8083063 Iustin Pop
    "name",
540 a8083063 Iustin Pop
    "path",
541 a8083063 Iustin Pop
    "api_version",
542 a8083063 Iustin Pop
    "create_script",
543 a8083063 Iustin Pop
    "export_script",
544 386b57af Iustin Pop
    "import_script",
545 386b57af Iustin Pop
    "rename_script",
546 a8083063 Iustin Pop
    ]
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
549 ec29fe40 Iustin Pop
class Node(TaggableObject):
550 a8083063 Iustin Pop
  """Config object representing a node."""
551 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
552 ec29fe40 Iustin Pop
    "name",
553 ec29fe40 Iustin Pop
    "primary_ip",
554 ec29fe40 Iustin Pop
    "secondary_ip",
555 ec29fe40 Iustin Pop
    ]
556 a8083063 Iustin Pop
557 a8083063 Iustin Pop
558 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
559 a8083063 Iustin Pop
  """Config object representing the cluster."""
560 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
561 a8083063 Iustin Pop
    "config_version",
562 a8083063 Iustin Pop
    "serial_no",
563 a8083063 Iustin Pop
    "rsahostkeypub",
564 a8083063 Iustin Pop
    "highest_used_port",
565 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
566 a8083063 Iustin Pop
    "mac_prefix",
567 a8083063 Iustin Pop
    "volume_group_name",
568 a8083063 Iustin Pop
    "default_bridge",
569 a8083063 Iustin Pop
    ]
570 a8083063 Iustin Pop
571 319856a9 Michael Hanselmann
  def ToDict(self):
572 319856a9 Michael Hanselmann
    """Custom function for cluster.
573 319856a9 Michael Hanselmann

574 319856a9 Michael Hanselmann
    """
575 319856a9 Michael Hanselmann
    mydict = super(TaggableObject, self).ToDict()
576 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
577 319856a9 Michael Hanselmann
    return mydict
578 319856a9 Michael Hanselmann
579 319856a9 Michael Hanselmann
  @classmethod
580 319856a9 Michael Hanselmann
  def FromDict(cls, val):
581 319856a9 Michael Hanselmann
    """Custom function for cluster.
582 319856a9 Michael Hanselmann

583 319856a9 Michael Hanselmann
    """
584 319856a9 Michael Hanselmann
    obj = super(TaggableObject, cls).FromDict(val)
585 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
586 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
587 319856a9 Michael Hanselmann
    return obj
588 319856a9 Michael Hanselmann
589 5c947f38 Iustin Pop
590 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
591 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
592 a8083063 Iustin Pop

593 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
594 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
595 a8083063 Iustin Pop
  buffer.
596 a8083063 Iustin Pop

597 a8083063 Iustin Pop
  """
598 a8083063 Iustin Pop
  def Dumps(self):
599 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
600 a8083063 Iustin Pop
    buf = StringIO()
601 a8083063 Iustin Pop
    self.write(buf)
602 a8083063 Iustin Pop
    return buf.getvalue()
603 a8083063 Iustin Pop
604 a8083063 Iustin Pop
  @staticmethod
605 a8083063 Iustin Pop
  def Loads(data):
606 a8083063 Iustin Pop
    """Load data from a string."""
607 a8083063 Iustin Pop
    buf = StringIO(data)
608 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
609 a8083063 Iustin Pop
    cfp.readfp(buf)
610 a8083063 Iustin Pop
    return cfp