Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 3f752d97

History | View | Annotate | Download (20.6 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 319856a9 Michael Hanselmann
import simplejson
31 a8083063 Iustin Pop
import ConfigParser
32 5c947f38 Iustin Pop
import re
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 a8083063 Iustin Pop
           "OS", "Node", "Cluster"]
41 a8083063 Iustin Pop
42 a8083063 Iustin Pop
43 d5835922 Michael Hanselmann
# Check whether the simplejson module supports indentation
44 d5835922 Michael Hanselmann
_JSON_INDENT = 2
45 d5835922 Michael Hanselmann
try:
46 d5835922 Michael Hanselmann
  simplejson.dumps(1, indent=_JSON_INDENT)
47 d5835922 Michael Hanselmann
except TypeError:
48 d5835922 Michael Hanselmann
  _JSON_INDENT = None
49 d5835922 Michael Hanselmann
50 d5835922 Michael Hanselmann
51 a8083063 Iustin Pop
class ConfigObject(object):
52 a8083063 Iustin Pop
  """A generic config object.
53 a8083063 Iustin Pop

54 a8083063 Iustin Pop
  It has the following properties:
55 a8083063 Iustin Pop

56 a8083063 Iustin Pop
    - provides somewhat safe recursive unpickling and pickling for its classes
57 a8083063 Iustin Pop
    - unset attributes which are defined in slots are always returned
58 a8083063 Iustin Pop
      as None instead of raising an error
59 a8083063 Iustin Pop

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

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

96 a8083063 Iustin Pop
    """
97 d5835922 Michael Hanselmann
    data = self.ToDict()
98 d5835922 Michael Hanselmann
    if _JSON_INDENT is None:
99 d5835922 Michael Hanselmann
      simplejson.dump(data, fobj)
100 d5835922 Michael Hanselmann
    else:
101 d5835922 Michael Hanselmann
      simplejson.dump(data, fobj, indent=_JSON_INDENT)
102 a8083063 Iustin Pop
103 319856a9 Michael Hanselmann
  @classmethod
104 319856a9 Michael Hanselmann
  def Load(cls, fobj):
105 319856a9 Michael Hanselmann
    """Load data from the given stream.
106 a8083063 Iustin Pop

107 a8083063 Iustin Pop
    """
108 319856a9 Michael Hanselmann
    return cls.FromDict(simplejson.load(fobj))
109 a8083063 Iustin Pop
110 a8083063 Iustin Pop
  def Dumps(self):
111 319856a9 Michael Hanselmann
    """Dump and return the string representation."""
112 a8083063 Iustin Pop
    buf = StringIO()
113 a8083063 Iustin Pop
    self.Dump(buf)
114 a8083063 Iustin Pop
    return buf.getvalue()
115 a8083063 Iustin Pop
116 319856a9 Michael Hanselmann
  @classmethod
117 319856a9 Michael Hanselmann
  def Loads(cls, data):
118 a8083063 Iustin Pop
    """Load data from a string."""
119 319856a9 Michael Hanselmann
    return cls.Load(StringIO(data))
120 a8083063 Iustin Pop
121 ff9c047c Iustin Pop
  def ToDict(self):
122 ff9c047c Iustin Pop
    """Convert to a dict holding only standard python types.
123 ff9c047c Iustin Pop

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

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

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

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

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

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

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

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

179 ff9c047c Iustin Pop
    """
180 ff9c047c Iustin Pop
    if not isinstance(c_type, type):
181 ff9c047c Iustin Pop
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
182 ff9c047c Iustin Pop
                      " not a type" % type(c_type))
183 ff9c047c Iustin Pop
    if c_type is dict:
184 ff9c047c Iustin Pop
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
185 ff9c047c Iustin Pop
    elif c_type in (list, tuple, set, frozenset):
186 ff9c047c Iustin Pop
      ret = c_type([e_type.FromDict(elem) for elem in source])
187 ff9c047c Iustin Pop
    else:
188 ff9c047c Iustin Pop
      raise TypeError("Invalid container type %s passed to"
189 ff9c047c Iustin Pop
                      " _ContainerFromDicts" % c_type)
190 ff9c047c Iustin Pop
    return ret
191 ff9c047c Iustin Pop
192 ff9c047c Iustin Pop
  def __repr__(self):
193 ff9c047c Iustin Pop
    """Implement __repr__ for ConfigObjects."""
194 ff9c047c Iustin Pop
    return repr(self.ToDict())
195 ff9c047c Iustin Pop
196 a8083063 Iustin Pop
197 ec29fe40 Iustin Pop
class TaggableObject(ConfigObject):
198 5c947f38 Iustin Pop
  """An generic class supporting tags.
199 5c947f38 Iustin Pop

200 5c947f38 Iustin Pop
  """
201 ec29fe40 Iustin Pop
  __slots__ = ConfigObject.__slots__ + ["tags"]
202 2057f6c7 Iustin Pop
203 5c947f38 Iustin Pop
  @staticmethod
204 5c947f38 Iustin Pop
  def ValidateTag(tag):
205 5c947f38 Iustin Pop
    """Check if a tag is valid.
206 5c947f38 Iustin Pop

207 5c947f38 Iustin Pop
    If the tag is invalid, an errors.TagError will be raised. The
208 5c947f38 Iustin Pop
    function has no return value.
209 5c947f38 Iustin Pop

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

224 5c947f38 Iustin Pop
    """
225 5c947f38 Iustin Pop
    tags = getattr(self, "tags", None)
226 5c947f38 Iustin Pop
    if tags is None:
227 5c947f38 Iustin Pop
      tags = self.tags = set()
228 5c947f38 Iustin Pop
    return tags
229 5c947f38 Iustin Pop
230 5c947f38 Iustin Pop
  def AddTag(self, tag):
231 5c947f38 Iustin Pop
    """Add a new tag.
232 5c947f38 Iustin Pop

233 5c947f38 Iustin Pop
    """
234 5c947f38 Iustin Pop
    self.ValidateTag(tag)
235 5c947f38 Iustin Pop
    tags = self.GetTags()
236 5c947f38 Iustin Pop
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
237 3ecf6786 Iustin Pop
      raise errors.TagError("Too many tags")
238 5c947f38 Iustin Pop
    self.GetTags().add(tag)
239 5c947f38 Iustin Pop
240 5c947f38 Iustin Pop
  def RemoveTag(self, tag):
241 5c947f38 Iustin Pop
    """Remove a tag.
242 5c947f38 Iustin Pop

243 5c947f38 Iustin Pop
    """
244 5c947f38 Iustin Pop
    self.ValidateTag(tag)
245 5c947f38 Iustin Pop
    tags = self.GetTags()
246 5c947f38 Iustin Pop
    try:
247 5c947f38 Iustin Pop
      tags.remove(tag)
248 5c947f38 Iustin Pop
    except KeyError:
249 3ecf6786 Iustin Pop
      raise errors.TagError("Tag not found")
250 5c947f38 Iustin Pop
251 ff9c047c Iustin Pop
  def ToDict(self):
252 ff9c047c Iustin Pop
    """Taggable-object-specific conversion to standard python types.
253 ff9c047c Iustin Pop

254 ff9c047c Iustin Pop
    This replaces the tags set with a list.
255 ff9c047c Iustin Pop

256 ff9c047c Iustin Pop
    """
257 ff9c047c Iustin Pop
    bo = super(TaggableObject, self).ToDict()
258 ff9c047c Iustin Pop
259 ff9c047c Iustin Pop
    tags = bo.get("tags", None)
260 ff9c047c Iustin Pop
    if isinstance(tags, set):
261 ff9c047c Iustin Pop
      bo["tags"] = list(tags)
262 ff9c047c Iustin Pop
    return bo
263 ff9c047c Iustin Pop
264 ff9c047c Iustin Pop
  @classmethod
265 ff9c047c Iustin Pop
  def FromDict(cls, val):
266 ff9c047c Iustin Pop
    """Custom function for instances.
267 ff9c047c Iustin Pop

268 ff9c047c Iustin Pop
    """
269 ff9c047c Iustin Pop
    obj = super(TaggableObject, cls).FromDict(val)
270 ff9c047c Iustin Pop
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
271 ff9c047c Iustin Pop
      obj.tags = set(obj.tags)
272 ff9c047c Iustin Pop
    return obj
273 ff9c047c Iustin Pop
274 5c947f38 Iustin Pop
275 a8083063 Iustin Pop
class ConfigData(ConfigObject):
276 a8083063 Iustin Pop
  """Top-level config object."""
277 b2fddf63 Iustin Pop
  __slots__ = ["cluster", "nodes", "instances"]
278 a8083063 Iustin Pop
279 ff9c047c Iustin Pop
  def ToDict(self):
280 ff9c047c Iustin Pop
    """Custom function for top-level config data.
281 ff9c047c Iustin Pop

282 ff9c047c Iustin Pop
    This just replaces the list of instances, nodes and the cluster
283 ff9c047c Iustin Pop
    with standard python types.
284 ff9c047c Iustin Pop

285 ff9c047c Iustin Pop
    """
286 ff9c047c Iustin Pop
    mydict = super(ConfigData, self).ToDict()
287 ff9c047c Iustin Pop
    mydict["cluster"] = mydict["cluster"].ToDict()
288 ff9c047c Iustin Pop
    for key in "nodes", "instances":
289 ff9c047c Iustin Pop
      mydict[key] = self._ContainerToDicts(mydict[key])
290 ff9c047c Iustin Pop
291 ff9c047c Iustin Pop
    return mydict
292 ff9c047c Iustin Pop
293 ff9c047c Iustin Pop
  @classmethod
294 ff9c047c Iustin Pop
  def FromDict(cls, val):
295 ff9c047c Iustin Pop
    """Custom function for top-level config data
296 ff9c047c Iustin Pop

297 ff9c047c Iustin Pop
    """
298 ff9c047c Iustin Pop
    obj = super(ConfigData, cls).FromDict(val)
299 ff9c047c Iustin Pop
    obj.cluster = Cluster.FromDict(obj.cluster)
300 ff9c047c Iustin Pop
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
301 ff9c047c Iustin Pop
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
302 ff9c047c Iustin Pop
    return obj
303 ff9c047c Iustin Pop
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
class NIC(ConfigObject):
306 a8083063 Iustin Pop
  """Config object representing a network card."""
307 a8083063 Iustin Pop
  __slots__ = ["mac", "ip", "bridge"]
308 a8083063 Iustin Pop
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
class Disk(ConfigObject):
311 a8083063 Iustin Pop
  """Config object representing a block device."""
312 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
313 a8083063 Iustin Pop
               "children", "iv_name", "size"]
314 a8083063 Iustin Pop
315 a8083063 Iustin Pop
  def CreateOnSecondary(self):
316 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
317 a1f445d3 Iustin Pop
    return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8,
318 a1f445d3 Iustin Pop
                             constants.LD_LV)
319 a8083063 Iustin Pop
320 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
321 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
322 a1f445d3 Iustin Pop
    return self.dev_type in (constants.LD_DRBD7, constants.LD_DRBD8,
323 a1f445d3 Iustin Pop
                             constants.LD_LV)
324 a8083063 Iustin Pop
325 a8083063 Iustin Pop
  def OpenOnSecondary(self):
326 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
327 fe96220b Iustin Pop
    return self.dev_type in (constants.LD_LV,)
328 a8083063 Iustin Pop
329 222f2dd5 Iustin Pop
  def StaticDevPath(self):
330 222f2dd5 Iustin Pop
    """Return the device path if this device type has a static one.
331 222f2dd5 Iustin Pop

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

336 222f2dd5 Iustin Pop
    """
337 222f2dd5 Iustin Pop
    if self.dev_type == constants.LD_LV:
338 222f2dd5 Iustin Pop
      return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
339 222f2dd5 Iustin Pop
    return None
340 222f2dd5 Iustin Pop
341 fc1dc9d7 Iustin Pop
  def ChildrenNeeded(self):
342 fc1dc9d7 Iustin Pop
    """Compute the needed number of children for activation.
343 fc1dc9d7 Iustin Pop

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

348 fc1dc9d7 Iustin Pop
    Currently, only DRBD8 supports diskless activation (therefore we
349 fc1dc9d7 Iustin Pop
    return 0), for all other we keep the previous semantics and return
350 fc1dc9d7 Iustin Pop
    -1.
351 fc1dc9d7 Iustin Pop

352 fc1dc9d7 Iustin Pop
    """
353 fc1dc9d7 Iustin Pop
    if self.dev_type == constants.LD_DRBD8:
354 fc1dc9d7 Iustin Pop
      return 0
355 fc1dc9d7 Iustin Pop
    return -1
356 fc1dc9d7 Iustin Pop
357 a8083063 Iustin Pop
  def GetNodes(self, node):
358 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
359 a8083063 Iustin Pop

360 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
361 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
362 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
363 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
364 a8083063 Iustin Pop

365 a8083063 Iustin Pop
    """
366 3f752d97 Manuel Franceschini
    if self.dev_type in [constants.LD_LV, constants.LD_MD_R1,
367 3f752d97 Manuel Franceschini
                         constants.LD_FILE]:
368 a8083063 Iustin Pop
      result = [node]
369 a1f445d3 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
370 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
371 a8083063 Iustin Pop
      if node not in result:
372 3ecf6786 Iustin Pop
        raise errors.ConfigurationError("DRBD device passed unknown node")
373 a8083063 Iustin Pop
    else:
374 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
375 a8083063 Iustin Pop
    return result
376 a8083063 Iustin Pop
377 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
378 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
379 a8083063 Iustin Pop

380 a8083063 Iustin Pop
    This method, given the node on which the parent disk lives, will
381 a8083063 Iustin Pop
    return the list of all (node, disk) pairs which describe the disk
382 a8083063 Iustin Pop
    tree in the most compact way. For example, a md/drbd/lvm stack
383 a8083063 Iustin Pop
    will be returned as (primary_node, md) and (secondary_node, drbd)
384 a8083063 Iustin Pop
    which represents all the top-level devices on the nodes. This
385 a8083063 Iustin Pop
    means that on the primary node we need to activate the the md (and
386 a8083063 Iustin Pop
    recursively all its children) and on the secondary node we need to
387 a8083063 Iustin Pop
    activate the drbd device (and its children, the two lvm volumes).
388 a8083063 Iustin Pop

389 a8083063 Iustin Pop
    """
390 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
391 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
392 a8083063 Iustin Pop
    if not self.children:
393 a8083063 Iustin Pop
      # leaf device
394 a8083063 Iustin Pop
      return result
395 a8083063 Iustin Pop
    for node in my_nodes:
396 a8083063 Iustin Pop
      for child in self.children:
397 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
398 a8083063 Iustin Pop
        if len(child_result) == 1:
399 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
400 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
401 a8083063 Iustin Pop
          # own entry for this node describes it completely
402 a8083063 Iustin Pop
          continue
403 a8083063 Iustin Pop
        else:
404 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
405 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
406 a8083063 Iustin Pop
          # one of its descendants
407 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
408 a8083063 Iustin Pop
            if subnode not in my_nodes:
409 a8083063 Iustin Pop
              result.append((subnode, subdisk))
410 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
411 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
412 a8083063 Iustin Pop
            # be different)
413 a8083063 Iustin Pop
    return result
414 a8083063 Iustin Pop
415 ff9c047c Iustin Pop
  def ToDict(self):
416 ff9c047c Iustin Pop
    """Disk-specific conversion to standard python types.
417 ff9c047c Iustin Pop

418 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of
419 ff9c047c Iustin Pop
    standard python types.
420 ff9c047c Iustin Pop

421 ff9c047c Iustin Pop
    """
422 ff9c047c Iustin Pop
    bo = super(Disk, self).ToDict()
423 ff9c047c Iustin Pop
424 ff9c047c Iustin Pop
    for attr in ("children",):
425 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
426 ff9c047c Iustin Pop
      if alist:
427 ff9c047c Iustin Pop
        bo[attr] = self._ContainerToDicts(alist)
428 ff9c047c Iustin Pop
    return bo
429 ff9c047c Iustin Pop
430 ff9c047c Iustin Pop
  @classmethod
431 ff9c047c Iustin Pop
  def FromDict(cls, val):
432 ff9c047c Iustin Pop
    """Custom function for Disks
433 ff9c047c Iustin Pop

434 ff9c047c Iustin Pop
    """
435 ff9c047c Iustin Pop
    obj = super(Disk, cls).FromDict(val)
436 ff9c047c Iustin Pop
    if obj.children:
437 ff9c047c Iustin Pop
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
438 ff9c047c Iustin Pop
    if obj.logical_id and isinstance(obj.logical_id, list):
439 ff9c047c Iustin Pop
      obj.logical_id = tuple(obj.logical_id)
440 ff9c047c Iustin Pop
    if obj.physical_id and isinstance(obj.physical_id, list):
441 ff9c047c Iustin Pop
      obj.physical_id = tuple(obj.physical_id)
442 ff9c047c Iustin Pop
    return obj
443 ff9c047c Iustin Pop
444 65a15336 Iustin Pop
  def __str__(self):
445 65a15336 Iustin Pop
    """Custom str() formatter for disks.
446 65a15336 Iustin Pop

447 65a15336 Iustin Pop
    """
448 65a15336 Iustin Pop
    if self.dev_type == constants.LD_LV:
449 65a15336 Iustin Pop
      val =  "<LogicalVolume(/dev/%s/%s" % self.logical_id
450 65a15336 Iustin Pop
    elif self.dev_type in constants.LDS_DRBD:
451 65a15336 Iustin Pop
      if self.dev_type == constants.LD_DRBD7:
452 65a15336 Iustin Pop
        val = "<DRBD7("
453 65a15336 Iustin Pop
      else:
454 65a15336 Iustin Pop
        val = "<DRBD8("
455 073ca59e Iustin Pop
      if self.physical_id is None:
456 073ca59e Iustin Pop
        phy = "unconfigured"
457 073ca59e Iustin Pop
      else:
458 073ca59e Iustin Pop
        phy = ("configured as %s:%s %s:%s" %
459 25a915d0 Iustin Pop
               (self.physical_id[0], self.physical_id[1],
460 25a915d0 Iustin Pop
                self.physical_id[2], self.physical_id[3]))
461 073ca59e Iustin Pop
462 073ca59e Iustin Pop
      val += ("hosts=%s-%s, port=%s, %s, " %
463 073ca59e Iustin Pop
              (self.logical_id[0], self.logical_id[1], self.logical_id[2],
464 073ca59e Iustin Pop
               phy))
465 65a15336 Iustin Pop
      if self.children and self.children.count(None) == 0:
466 65a15336 Iustin Pop
        val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
467 65a15336 Iustin Pop
      else:
468 65a15336 Iustin Pop
        val += "no local storage"
469 65a15336 Iustin Pop
    elif self.dev_type == constants.LD_MD_R1:
470 65a15336 Iustin Pop
      val = "<MD_R1(uuid=%s, children=%s" % (self.physical_id, self.children)
471 65a15336 Iustin Pop
    else:
472 65a15336 Iustin Pop
      val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
473 65a15336 Iustin Pop
             (self.dev_type, self.logical_id, self.physical_id, self.children))
474 65a15336 Iustin Pop
    if self.iv_name is None:
475 65a15336 Iustin Pop
      val += ", not visible"
476 65a15336 Iustin Pop
    else:
477 65a15336 Iustin Pop
      val += ", visible as /dev/%s" % self.iv_name
478 65a15336 Iustin Pop
    val += ", size=%dm)>" % self.size
479 65a15336 Iustin Pop
    return val
480 65a15336 Iustin Pop
481 a8083063 Iustin Pop
482 ec29fe40 Iustin Pop
class Instance(TaggableObject):
483 a8083063 Iustin Pop
  """Config object representing an instance."""
484 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
485 a8083063 Iustin Pop
    "name",
486 a8083063 Iustin Pop
    "primary_node",
487 a8083063 Iustin Pop
    "os",
488 a8083063 Iustin Pop
    "status",
489 a8083063 Iustin Pop
    "memory",
490 a8083063 Iustin Pop
    "vcpus",
491 a8083063 Iustin Pop
    "nics",
492 a8083063 Iustin Pop
    "disks",
493 a8083063 Iustin Pop
    "disk_template",
494 58acb49d Alexander Schreiber
    "network_port",
495 2d8c9c2e Iustin Pop
    "kernel_path",
496 2d8c9c2e Iustin Pop
    "initrd_path",
497 0d9c53bc Alexander Schreiber
    "hvm_boot_order",
498 a8083063 Iustin Pop
    ]
499 a8083063 Iustin Pop
500 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
501 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
502 a8083063 Iustin Pop

503 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
504 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
505 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
506 a8083063 Iustin Pop
    dynamically.
507 a8083063 Iustin Pop

508 a8083063 Iustin Pop
    """
509 a8083063 Iustin Pop
    def _Helper(primary, sec_nodes, device):
510 a8083063 Iustin Pop
      """Recursively computes secondary nodes given a top device."""
511 a1f445d3 Iustin Pop
      if device.dev_type in constants.LDS_DRBD:
512 a8083063 Iustin Pop
        nodea, nodeb, dummy = device.logical_id
513 a8083063 Iustin Pop
        if nodea == primary:
514 a8083063 Iustin Pop
          candidate = nodeb
515 a8083063 Iustin Pop
        else:
516 a8083063 Iustin Pop
          candidate = nodea
517 a8083063 Iustin Pop
        if candidate not in sec_nodes:
518 a8083063 Iustin Pop
          sec_nodes.append(candidate)
519 a8083063 Iustin Pop
      if device.children:
520 a8083063 Iustin Pop
        for child in device.children:
521 a8083063 Iustin Pop
          _Helper(primary, sec_nodes, child)
522 a8083063 Iustin Pop
523 a8083063 Iustin Pop
    secondary_nodes = []
524 a8083063 Iustin Pop
    for device in self.disks:
525 a8083063 Iustin Pop
      _Helper(self.primary_node, secondary_nodes, device)
526 a8083063 Iustin Pop
    return tuple(secondary_nodes)
527 a8083063 Iustin Pop
528 a8083063 Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
529 a8083063 Iustin Pop
                             "List of secondary nodes")
530 a8083063 Iustin Pop
531 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
532 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
533 a8083063 Iustin Pop

534 a8083063 Iustin Pop
    This function figures out what logical volumes should belong on which
535 a8083063 Iustin Pop
    nodes, recursing through a device tree.
536 a8083063 Iustin Pop

537 a8083063 Iustin Pop
    Args:
538 a8083063 Iustin Pop
      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
539 a8083063 Iustin Pop

540 a8083063 Iustin Pop
    Returns:
541 a8083063 Iustin Pop
      None if lvmap arg is given.
542 a8083063 Iustin Pop
      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
543 a8083063 Iustin Pop

544 a8083063 Iustin Pop
    """
545 a8083063 Iustin Pop
    if node == None:
546 a8083063 Iustin Pop
      node = self.primary_node
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
    if lvmap is None:
549 a8083063 Iustin Pop
      lvmap = { node : [] }
550 a8083063 Iustin Pop
      ret = lvmap
551 a8083063 Iustin Pop
    else:
552 a8083063 Iustin Pop
      if not node in lvmap:
553 a8083063 Iustin Pop
        lvmap[node] = []
554 a8083063 Iustin Pop
      ret = None
555 a8083063 Iustin Pop
556 a8083063 Iustin Pop
    if not devs:
557 a8083063 Iustin Pop
      devs = self.disks
558 a8083063 Iustin Pop
559 a8083063 Iustin Pop
    for dev in devs:
560 fe96220b Iustin Pop
      if dev.dev_type == constants.LD_LV:
561 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
562 a8083063 Iustin Pop
563 a1f445d3 Iustin Pop
      elif dev.dev_type in constants.LDS_DRBD:
564 a8083063 Iustin Pop
        if dev.logical_id[0] not in lvmap:
565 a8083063 Iustin Pop
          lvmap[dev.logical_id[0]] = []
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
        if dev.logical_id[1] not in lvmap:
568 a8083063 Iustin Pop
          lvmap[dev.logical_id[1]] = []
569 a8083063 Iustin Pop
570 a8083063 Iustin Pop
        if dev.children:
571 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
572 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
      elif dev.children:
575 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
576 a8083063 Iustin Pop
577 a8083063 Iustin Pop
    return ret
578 a8083063 Iustin Pop
579 644eeef9 Iustin Pop
  def FindDisk(self, name):
580 644eeef9 Iustin Pop
    """Find a disk given having a specified name.
581 644eeef9 Iustin Pop

582 644eeef9 Iustin Pop
    This will return the disk which has the given iv_name.
583 644eeef9 Iustin Pop

584 644eeef9 Iustin Pop
    """
585 644eeef9 Iustin Pop
    for disk in self.disks:
586 644eeef9 Iustin Pop
      if disk.iv_name == name:
587 644eeef9 Iustin Pop
        return disk
588 644eeef9 Iustin Pop
589 644eeef9 Iustin Pop
    return None
590 644eeef9 Iustin Pop
591 ff9c047c Iustin Pop
  def ToDict(self):
592 ff9c047c Iustin Pop
    """Instance-specific conversion to standard python types.
593 ff9c047c Iustin Pop

594 ff9c047c Iustin Pop
    This replaces the children lists of objects with lists of standard
595 ff9c047c Iustin Pop
    python types.
596 ff9c047c Iustin Pop

597 ff9c047c Iustin Pop
    """
598 ff9c047c Iustin Pop
    bo = super(Instance, self).ToDict()
599 ff9c047c Iustin Pop
600 ff9c047c Iustin Pop
    for attr in "nics", "disks":
601 ff9c047c Iustin Pop
      alist = bo.get(attr, None)
602 ff9c047c Iustin Pop
      if alist:
603 ff9c047c Iustin Pop
        nlist = self._ContainerToDicts(alist)
604 ff9c047c Iustin Pop
      else:
605 ff9c047c Iustin Pop
        nlist = []
606 ff9c047c Iustin Pop
      bo[attr] = nlist
607 ff9c047c Iustin Pop
    return bo
608 ff9c047c Iustin Pop
609 ff9c047c Iustin Pop
  @classmethod
610 ff9c047c Iustin Pop
  def FromDict(cls, val):
611 ff9c047c Iustin Pop
    """Custom function for instances.
612 ff9c047c Iustin Pop

613 ff9c047c Iustin Pop
    """
614 ff9c047c Iustin Pop
    obj = super(Instance, cls).FromDict(val)
615 ff9c047c Iustin Pop
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
616 ff9c047c Iustin Pop
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
617 ff9c047c Iustin Pop
    return obj
618 ff9c047c Iustin Pop
619 a8083063 Iustin Pop
620 a8083063 Iustin Pop
class OS(ConfigObject):
621 a8083063 Iustin Pop
  """Config object representing an operating system."""
622 a8083063 Iustin Pop
  __slots__ = [
623 a8083063 Iustin Pop
    "name",
624 a8083063 Iustin Pop
    "path",
625 37482e7b Guido Trotter
    "status",
626 a8083063 Iustin Pop
    "api_version",
627 a8083063 Iustin Pop
    "create_script",
628 a8083063 Iustin Pop
    "export_script",
629 386b57af Iustin Pop
    "import_script",
630 386b57af Iustin Pop
    "rename_script",
631 a8083063 Iustin Pop
    ]
632 a8083063 Iustin Pop
633 d2c807e4 Guido Trotter
  @classmethod
634 d2c807e4 Guido Trotter
  def FromInvalidOS(cls, err):
635 d2c807e4 Guido Trotter
    """Create an OS from an InvalidOS error.
636 d2c807e4 Guido Trotter

637 d2c807e4 Guido Trotter
    This routine knows how to convert an InvalidOS error to an OS
638 d2c807e4 Guido Trotter
    object representing the broken OS with a meaningful error message.
639 d2c807e4 Guido Trotter

640 d2c807e4 Guido Trotter
    """
641 d2c807e4 Guido Trotter
    if not isinstance(err, errors.InvalidOS):
642 d2c807e4 Guido Trotter
      raise errors.ProgrammerError("Trying to initialize an OS from an"
643 d2c807e4 Guido Trotter
                                   " invalid object of type %s" % type(err))
644 d2c807e4 Guido Trotter
645 d2c807e4 Guido Trotter
    return cls(name=err.args[0], path=err.args[1], status=err.args[2])
646 d2c807e4 Guido Trotter
647 37482e7b Guido Trotter
  def __nonzero__(self):
648 37482e7b Guido Trotter
    return self.status == constants.OS_VALID_STATUS
649 37482e7b Guido Trotter
650 37482e7b Guido Trotter
  __bool__ = __nonzero__
651 a8083063 Iustin Pop
652 7c0d6283 Michael Hanselmann
653 ec29fe40 Iustin Pop
class Node(TaggableObject):
654 a8083063 Iustin Pop
  """Config object representing a node."""
655 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
656 ec29fe40 Iustin Pop
    "name",
657 ec29fe40 Iustin Pop
    "primary_ip",
658 ec29fe40 Iustin Pop
    "secondary_ip",
659 ec29fe40 Iustin Pop
    ]
660 a8083063 Iustin Pop
661 a8083063 Iustin Pop
662 ec29fe40 Iustin Pop
class Cluster(TaggableObject):
663 a8083063 Iustin Pop
  """Config object representing the cluster."""
664 ec29fe40 Iustin Pop
  __slots__ = TaggableObject.__slots__ + [
665 a8083063 Iustin Pop
    "config_version",
666 a8083063 Iustin Pop
    "serial_no",
667 a8083063 Iustin Pop
    "rsahostkeypub",
668 a8083063 Iustin Pop
    "highest_used_port",
669 b2fddf63 Iustin Pop
    "tcpudp_port_pool",
670 a8083063 Iustin Pop
    "mac_prefix",
671 a8083063 Iustin Pop
    "volume_group_name",
672 a8083063 Iustin Pop
    "default_bridge",
673 a8083063 Iustin Pop
    ]
674 a8083063 Iustin Pop
675 319856a9 Michael Hanselmann
  def ToDict(self):
676 319856a9 Michael Hanselmann
    """Custom function for cluster.
677 319856a9 Michael Hanselmann

678 319856a9 Michael Hanselmann
    """
679 b60ae2ca Iustin Pop
    mydict = super(Cluster, self).ToDict()
680 319856a9 Michael Hanselmann
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
681 319856a9 Michael Hanselmann
    return mydict
682 319856a9 Michael Hanselmann
683 319856a9 Michael Hanselmann
  @classmethod
684 319856a9 Michael Hanselmann
  def FromDict(cls, val):
685 319856a9 Michael Hanselmann
    """Custom function for cluster.
686 319856a9 Michael Hanselmann

687 319856a9 Michael Hanselmann
    """
688 b60ae2ca Iustin Pop
    obj = super(Cluster, cls).FromDict(val)
689 319856a9 Michael Hanselmann
    if not isinstance(obj.tcpudp_port_pool, set):
690 319856a9 Michael Hanselmann
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
691 319856a9 Michael Hanselmann
    return obj
692 319856a9 Michael Hanselmann
693 5c947f38 Iustin Pop
694 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
695 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
696 a8083063 Iustin Pop

697 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
698 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
699 a8083063 Iustin Pop
  buffer.
700 a8083063 Iustin Pop

701 a8083063 Iustin Pop
  """
702 a8083063 Iustin Pop
  def Dumps(self):
703 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
704 a8083063 Iustin Pop
    buf = StringIO()
705 a8083063 Iustin Pop
    self.write(buf)
706 a8083063 Iustin Pop
    return buf.getvalue()
707 a8083063 Iustin Pop
708 a8083063 Iustin Pop
  @staticmethod
709 a8083063 Iustin Pop
  def Loads(data):
710 a8083063 Iustin Pop
    """Load data from a string."""
711 a8083063 Iustin Pop
    buf = StringIO(data)
712 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
713 a8083063 Iustin Pop
    cfp.readfp(buf)
714 a8083063 Iustin Pop
    return cfp