Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ 5b7b5d49

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

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

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

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

53 a8083063 Iustin Pop
  """
54 a8083063 Iustin Pop
  __slots__ = []
55 a8083063 Iustin Pop
56 a8083063 Iustin Pop
  def __init__(self, **kwargs):
57 a8083063 Iustin Pop
    for i in kwargs:
58 a8083063 Iustin Pop
      setattr(self, i, kwargs[i])
59 a8083063 Iustin Pop
60 a8083063 Iustin Pop
  def __getattr__(self, name):
61 a8083063 Iustin Pop
    if name not in self.__slots__:
62 a8083063 Iustin Pop
      raise AttributeError, ("Invalid object attribute %s.%s" %
63 a8083063 Iustin Pop
                             (type(self).__name__, name))
64 a8083063 Iustin Pop
    return None
65 a8083063 Iustin Pop
66 a8083063 Iustin Pop
  def __getstate__(self):
67 a8083063 Iustin Pop
    state = {}
68 a8083063 Iustin Pop
    for name in self.__slots__:
69 a8083063 Iustin Pop
      if hasattr(self, name):
70 a8083063 Iustin Pop
        state[name] = getattr(self, name)
71 a8083063 Iustin Pop
    return state
72 a8083063 Iustin Pop
73 a8083063 Iustin Pop
  def __setstate__(self, state):
74 a8083063 Iustin Pop
    for name in state:
75 a8083063 Iustin Pop
      if name in self.__slots__:
76 a8083063 Iustin Pop
        setattr(self, name, state[name])
77 a8083063 Iustin Pop
78 a8083063 Iustin Pop
  @staticmethod
79 a8083063 Iustin Pop
  def FindGlobal(module, name):
80 a8083063 Iustin Pop
    """Function filtering the allowed classes to be un-pickled.
81 a8083063 Iustin Pop

82 a8083063 Iustin Pop
    Currently, we only allow the classes from this module which are
83 a8083063 Iustin Pop
    derived from ConfigObject.
84 a8083063 Iustin Pop

85 a8083063 Iustin Pop
    """
86 a8083063 Iustin Pop
    # Also support the old module name (ganeti.config)
87 a8083063 Iustin Pop
    cls = None
88 a8083063 Iustin Pop
    if module == "ganeti.config" or module == "ganeti.objects":
89 a8083063 Iustin Pop
      if name == "ConfigData":
90 a8083063 Iustin Pop
        cls = ConfigData
91 a8083063 Iustin Pop
      elif name == "NIC":
92 a8083063 Iustin Pop
        cls = NIC
93 a8083063 Iustin Pop
      elif name == "Disk" or name == "BlockDev":
94 a8083063 Iustin Pop
        cls = Disk
95 a8083063 Iustin Pop
      elif name == "Instance":
96 a8083063 Iustin Pop
        cls = Instance
97 a8083063 Iustin Pop
      elif name == "OS":
98 a8083063 Iustin Pop
        cls = OS
99 a8083063 Iustin Pop
      elif name == "Node":
100 a8083063 Iustin Pop
        cls = Node
101 a8083063 Iustin Pop
      elif name == "Cluster":
102 a8083063 Iustin Pop
        cls = Cluster
103 a8083063 Iustin Pop
    if cls is None:
104 a8083063 Iustin Pop
      raise cPickle.UnpicklingError, ("Class %s.%s not allowed due to"
105 a8083063 Iustin Pop
                                      " security concerns" % (module, name))
106 a8083063 Iustin Pop
    return cls
107 a8083063 Iustin Pop
108 a8083063 Iustin Pop
  def Dump(self, fobj):
109 a8083063 Iustin Pop
    """Dump this instance to a file object.
110 a8083063 Iustin Pop

111 a8083063 Iustin Pop
    Note that we use the HIGHEST_PROTOCOL, as it brings benefits for
112 a8083063 Iustin Pop
    the new classes.
113 a8083063 Iustin Pop

114 a8083063 Iustin Pop
    """
115 a8083063 Iustin Pop
    dumper = cPickle.Pickler(fobj, cPickle.HIGHEST_PROTOCOL)
116 a8083063 Iustin Pop
    dumper.dump(self)
117 a8083063 Iustin Pop
118 a8083063 Iustin Pop
  @staticmethod
119 a8083063 Iustin Pop
  def Load(fobj):
120 a8083063 Iustin Pop
    """Unpickle data from the given stream.
121 a8083063 Iustin Pop

122 a8083063 Iustin Pop
    This uses the `FindGlobal` function to filter the allowed classes.
123 a8083063 Iustin Pop

124 a8083063 Iustin Pop
    """
125 a8083063 Iustin Pop
    loader = cPickle.Unpickler(fobj)
126 a8083063 Iustin Pop
    loader.find_global = ConfigObject.FindGlobal
127 a8083063 Iustin Pop
    return loader.load()
128 a8083063 Iustin Pop
129 a8083063 Iustin Pop
  def Dumps(self):
130 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
131 a8083063 Iustin Pop
    buf = StringIO()
132 a8083063 Iustin Pop
    self.Dump(buf)
133 a8083063 Iustin Pop
    return buf.getvalue()
134 a8083063 Iustin Pop
135 a8083063 Iustin Pop
  @staticmethod
136 a8083063 Iustin Pop
  def Loads(data):
137 a8083063 Iustin Pop
    """Load data from a string."""
138 a8083063 Iustin Pop
    return ConfigObject.Load(StringIO(data))
139 a8083063 Iustin Pop
140 a8083063 Iustin Pop
141 a8083063 Iustin Pop
class ConfigData(ConfigObject):
142 a8083063 Iustin Pop
  """Top-level config object."""
143 a8083063 Iustin Pop
  __slots__ = ["cluster", "nodes", "instances"]
144 a8083063 Iustin Pop
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
class NIC(ConfigObject):
147 a8083063 Iustin Pop
  """Config object representing a network card."""
148 a8083063 Iustin Pop
  __slots__ = ["mac", "ip", "bridge"]
149 a8083063 Iustin Pop
150 a8083063 Iustin Pop
151 a8083063 Iustin Pop
class Disk(ConfigObject):
152 a8083063 Iustin Pop
  """Config object representing a block device."""
153 a8083063 Iustin Pop
  __slots__ = ["dev_type", "logical_id", "physical_id",
154 a8083063 Iustin Pop
               "children", "iv_name", "size"]
155 a8083063 Iustin Pop
156 a8083063 Iustin Pop
  def CreateOnSecondary(self):
157 a8083063 Iustin Pop
    """Test if this device needs to be created on a secondary node."""
158 a8083063 Iustin Pop
    return self.dev_type in ("drbd", "lvm")
159 a8083063 Iustin Pop
160 a8083063 Iustin Pop
  def AssembleOnSecondary(self):
161 a8083063 Iustin Pop
    """Test if this device needs to be assembled on a secondary node."""
162 a8083063 Iustin Pop
    return self.dev_type in ("drbd", "lvm")
163 a8083063 Iustin Pop
164 a8083063 Iustin Pop
  def OpenOnSecondary(self):
165 a8083063 Iustin Pop
    """Test if this device needs to be opened on a secondary node."""
166 a8083063 Iustin Pop
    return self.dev_type in ("lvm",)
167 a8083063 Iustin Pop
168 a8083063 Iustin Pop
  def GetNodes(self, node):
169 a8083063 Iustin Pop
    """This function returns the nodes this device lives on.
170 a8083063 Iustin Pop

171 a8083063 Iustin Pop
    Given the node on which the parent of the device lives on (or, in
172 a8083063 Iustin Pop
    case of a top-level device, the primary node of the devices'
173 a8083063 Iustin Pop
    instance), this function will return a list of nodes on which this
174 a8083063 Iustin Pop
    devices needs to (or can) be assembled.
175 a8083063 Iustin Pop

176 a8083063 Iustin Pop
    """
177 a8083063 Iustin Pop
    if self.dev_type == "lvm" or self.dev_type == "md_raid1":
178 a8083063 Iustin Pop
      result = [node]
179 a8083063 Iustin Pop
    elif self.dev_type == "drbd":
180 a8083063 Iustin Pop
      result = [self.logical_id[0], self.logical_id[1]]
181 a8083063 Iustin Pop
      if node not in result:
182 a8083063 Iustin Pop
        raise errors.ConfigurationError, ("DRBD device passed unknown node")
183 a8083063 Iustin Pop
    else:
184 a8083063 Iustin Pop
      raise errors.ProgrammerError, "Unhandled device type %s" % self.dev_type
185 a8083063 Iustin Pop
    return result
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
  def ComputeNodeTree(self, parent_node):
188 a8083063 Iustin Pop
    """Compute the node/disk tree for this disk and its children.
189 a8083063 Iustin Pop

190 a8083063 Iustin Pop
    This method, given the node on which the parent disk lives, will
191 a8083063 Iustin Pop
    return the list of all (node, disk) pairs which describe the disk
192 a8083063 Iustin Pop
    tree in the most compact way. For example, a md/drbd/lvm stack
193 a8083063 Iustin Pop
    will be returned as (primary_node, md) and (secondary_node, drbd)
194 a8083063 Iustin Pop
    which represents all the top-level devices on the nodes. This
195 a8083063 Iustin Pop
    means that on the primary node we need to activate the the md (and
196 a8083063 Iustin Pop
    recursively all its children) and on the secondary node we need to
197 a8083063 Iustin Pop
    activate the drbd device (and its children, the two lvm volumes).
198 a8083063 Iustin Pop

199 a8083063 Iustin Pop
    """
200 a8083063 Iustin Pop
    my_nodes = self.GetNodes(parent_node)
201 a8083063 Iustin Pop
    result = [(node, self) for node in my_nodes]
202 a8083063 Iustin Pop
    if not self.children:
203 a8083063 Iustin Pop
      # leaf device
204 a8083063 Iustin Pop
      return result
205 a8083063 Iustin Pop
    for node in my_nodes:
206 a8083063 Iustin Pop
      for child in self.children:
207 a8083063 Iustin Pop
        child_result = child.ComputeNodeTree(node)
208 a8083063 Iustin Pop
        if len(child_result) == 1:
209 a8083063 Iustin Pop
          # child (and all its descendants) is simple, doesn't split
210 a8083063 Iustin Pop
          # over multiple hosts, so we don't need to describe it, our
211 a8083063 Iustin Pop
          # own entry for this node describes it completely
212 a8083063 Iustin Pop
          continue
213 a8083063 Iustin Pop
        else:
214 a8083063 Iustin Pop
          # check if child nodes differ from my nodes; note that
215 a8083063 Iustin Pop
          # subdisk can differ from the child itself, and be instead
216 a8083063 Iustin Pop
          # one of its descendants
217 a8083063 Iustin Pop
          for subnode, subdisk in child_result:
218 a8083063 Iustin Pop
            if subnode not in my_nodes:
219 a8083063 Iustin Pop
              result.append((subnode, subdisk))
220 a8083063 Iustin Pop
            # otherwise child is under our own node, so we ignore this
221 a8083063 Iustin Pop
            # entry (but probably the other results in the list will
222 a8083063 Iustin Pop
            # be different)
223 a8083063 Iustin Pop
    return result
224 a8083063 Iustin Pop
225 a8083063 Iustin Pop
226 a8083063 Iustin Pop
class Instance(ConfigObject):
227 a8083063 Iustin Pop
  """Config object representing an instance."""
228 a8083063 Iustin Pop
  __slots__ = [
229 a8083063 Iustin Pop
    "name",
230 a8083063 Iustin Pop
    "primary_node",
231 a8083063 Iustin Pop
    "os",
232 a8083063 Iustin Pop
    "status",
233 a8083063 Iustin Pop
    "memory",
234 a8083063 Iustin Pop
    "vcpus",
235 a8083063 Iustin Pop
    "nics",
236 a8083063 Iustin Pop
    "disks",
237 a8083063 Iustin Pop
    "disk_template",
238 a8083063 Iustin Pop
    ]
239 a8083063 Iustin Pop
240 a8083063 Iustin Pop
  def _ComputeSecondaryNodes(self):
241 a8083063 Iustin Pop
    """Compute the list of secondary nodes.
242 a8083063 Iustin Pop

243 a8083063 Iustin Pop
    Since the data is already there (in the drbd disks), keeping it as
244 a8083063 Iustin Pop
    a separate normal attribute is redundant and if not properly
245 a8083063 Iustin Pop
    synchronised can cause problems. Thus it's better to compute it
246 a8083063 Iustin Pop
    dynamically.
247 a8083063 Iustin Pop

248 a8083063 Iustin Pop
    """
249 a8083063 Iustin Pop
    def _Helper(primary, sec_nodes, device):
250 a8083063 Iustin Pop
      """Recursively computes secondary nodes given a top device."""
251 a8083063 Iustin Pop
      if device.dev_type == 'drbd':
252 a8083063 Iustin Pop
        nodea, nodeb, dummy = device.logical_id
253 a8083063 Iustin Pop
        if nodea == primary:
254 a8083063 Iustin Pop
          candidate = nodeb
255 a8083063 Iustin Pop
        else:
256 a8083063 Iustin Pop
          candidate = nodea
257 a8083063 Iustin Pop
        if candidate not in sec_nodes:
258 a8083063 Iustin Pop
          sec_nodes.append(candidate)
259 a8083063 Iustin Pop
      if device.children:
260 a8083063 Iustin Pop
        for child in device.children:
261 a8083063 Iustin Pop
          _Helper(primary, sec_nodes, child)
262 a8083063 Iustin Pop
263 a8083063 Iustin Pop
    secondary_nodes = []
264 a8083063 Iustin Pop
    for device in self.disks:
265 a8083063 Iustin Pop
      _Helper(self.primary_node, secondary_nodes, device)
266 a8083063 Iustin Pop
    return tuple(secondary_nodes)
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
  secondary_nodes = property(_ComputeSecondaryNodes, None, None,
269 a8083063 Iustin Pop
                             "List of secondary nodes")
270 a8083063 Iustin Pop
271 a8083063 Iustin Pop
  def MapLVsByNode(self, lvmap=None, devs=None, node=None):
272 a8083063 Iustin Pop
    """Provide a mapping of nodes to LVs this instance owns.
273 a8083063 Iustin Pop

274 a8083063 Iustin Pop
    This function figures out what logical volumes should belong on which
275 a8083063 Iustin Pop
    nodes, recursing through a device tree.
276 a8083063 Iustin Pop

277 a8083063 Iustin Pop
    Args:
278 a8083063 Iustin Pop
      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
    Returns:
281 a8083063 Iustin Pop
      None if lvmap arg is given.
282 a8083063 Iustin Pop
      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
283 a8083063 Iustin Pop

284 a8083063 Iustin Pop
    """
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
    if node == None:
287 a8083063 Iustin Pop
      node = self.primary_node
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
    if lvmap is None:
290 a8083063 Iustin Pop
      lvmap = { node : [] }
291 a8083063 Iustin Pop
      ret = lvmap
292 a8083063 Iustin Pop
    else:
293 a8083063 Iustin Pop
      if not node in lvmap:
294 a8083063 Iustin Pop
        lvmap[node] = []
295 a8083063 Iustin Pop
      ret = None
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
    if not devs:
298 a8083063 Iustin Pop
      devs = self.disks
299 a8083063 Iustin Pop
300 a8083063 Iustin Pop
    for dev in devs:
301 a8083063 Iustin Pop
      if dev.dev_type == "lvm":
302 a8083063 Iustin Pop
        lvmap[node].append(dev.logical_id[1])
303 a8083063 Iustin Pop
304 a8083063 Iustin Pop
      elif dev.dev_type == "drbd":
305 a8083063 Iustin Pop
        if dev.logical_id[0] not in lvmap:
306 a8083063 Iustin Pop
          lvmap[dev.logical_id[0]] = []
307 a8083063 Iustin Pop
308 a8083063 Iustin Pop
        if dev.logical_id[1] not in lvmap:
309 a8083063 Iustin Pop
          lvmap[dev.logical_id[1]] = []
310 a8083063 Iustin Pop
311 a8083063 Iustin Pop
        if dev.children:
312 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
313 a8083063 Iustin Pop
          self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
314 a8083063 Iustin Pop
315 a8083063 Iustin Pop
      elif dev.children:
316 a8083063 Iustin Pop
        self.MapLVsByNode(lvmap, dev.children, node)
317 a8083063 Iustin Pop
318 a8083063 Iustin Pop
    return ret
319 a8083063 Iustin Pop
320 a8083063 Iustin Pop
321 a8083063 Iustin Pop
class OS(ConfigObject):
322 a8083063 Iustin Pop
  """Config object representing an operating system."""
323 a8083063 Iustin Pop
  __slots__ = [
324 a8083063 Iustin Pop
    "name",
325 a8083063 Iustin Pop
    "path",
326 a8083063 Iustin Pop
    "api_version",
327 a8083063 Iustin Pop
    "create_script",
328 a8083063 Iustin Pop
    "export_script",
329 a8083063 Iustin Pop
    "import_script"
330 a8083063 Iustin Pop
    ]
331 a8083063 Iustin Pop
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
class Node(ConfigObject):
334 a8083063 Iustin Pop
  """Config object representing a node."""
335 a8083063 Iustin Pop
  __slots__ = ["name", "primary_ip", "secondary_ip"]
336 a8083063 Iustin Pop
337 a8083063 Iustin Pop
338 a8083063 Iustin Pop
class Cluster(ConfigObject):
339 a8083063 Iustin Pop
  """Config object representing the cluster."""
340 a8083063 Iustin Pop
  __slots__ = [
341 a8083063 Iustin Pop
    "config_version",
342 a8083063 Iustin Pop
    "serial_no",
343 a8083063 Iustin Pop
    "master_node",
344 a8083063 Iustin Pop
    "name",
345 a8083063 Iustin Pop
    "rsahostkeypub",
346 a8083063 Iustin Pop
    "highest_used_port",
347 a8083063 Iustin Pop
    "mac_prefix",
348 a8083063 Iustin Pop
    "volume_group_name",
349 a8083063 Iustin Pop
    "default_bridge",
350 a8083063 Iustin Pop
    ]
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
class SerializableConfigParser(ConfigParser.SafeConfigParser):
353 a8083063 Iustin Pop
  """Simple wrapper over ConfigParse that allows serialization.
354 a8083063 Iustin Pop

355 a8083063 Iustin Pop
  This class is basically ConfigParser.SafeConfigParser with two
356 a8083063 Iustin Pop
  additional methods that allow it to serialize/unserialize to/from a
357 a8083063 Iustin Pop
  buffer.
358 a8083063 Iustin Pop

359 a8083063 Iustin Pop
  """
360 a8083063 Iustin Pop
  def Dumps(self):
361 a8083063 Iustin Pop
    """Dump this instance and return the string representation."""
362 a8083063 Iustin Pop
    buf = StringIO()
363 a8083063 Iustin Pop
    self.write(buf)
364 a8083063 Iustin Pop
    return buf.getvalue()
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
  @staticmethod
367 a8083063 Iustin Pop
  def Loads(data):
368 a8083063 Iustin Pop
    """Load data from a string."""
369 a8083063 Iustin Pop
    buf = StringIO(data)
370 a8083063 Iustin Pop
    cfp = SerializableConfigParser()
371 a8083063 Iustin Pop
    cfp.readfp(buf)
372 a8083063 Iustin Pop
    return cfp