Statistics
| Branch: | Tag: | Revision:

root / lib / objects.py @ b2fddf63

History | View | Annotate | Download (10.7 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 264bb3c5 Michael Hanselmann
    elif module == "__builtin__":
104 264bb3c5 Michael Hanselmann
      if name == "set":
105 264bb3c5 Michael Hanselmann
        cls = set
106 a8083063 Iustin Pop
    if cls is None:
107 a8083063 Iustin Pop
      raise cPickle.UnpicklingError, ("Class %s.%s not allowed due to"
108 a8083063 Iustin Pop
                                      " security concerns" % (module, name))
109 a8083063 Iustin Pop
    return cls
110 a8083063 Iustin Pop
111 a8083063 Iustin Pop
  def Dump(self, fobj):
112 a8083063 Iustin Pop
    """Dump this instance to a file object.
113 a8083063 Iustin Pop

114 a8083063 Iustin Pop
    Note that we use the HIGHEST_PROTOCOL, as it brings benefits for
115 a8083063 Iustin Pop
    the new classes.
116 a8083063 Iustin Pop

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

125 a8083063 Iustin Pop
    This uses the `FindGlobal` function to filter the allowed classes.
126 a8083063 Iustin Pop

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

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

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

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

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

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

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

277 a8083063 Iustin Pop
    This function figures out what logical volumes should belong on which
278 a8083063 Iustin Pop
    nodes, recursing through a device tree.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
    Args:
281 a8083063 Iustin Pop
      lvmap: (optional) a dictionary to receive the 'node' : ['lv', ...] data.
282 a8083063 Iustin Pop

283 a8083063 Iustin Pop
    Returns:
284 a8083063 Iustin Pop
      None if lvmap arg is given.
285 a8083063 Iustin Pop
      Otherwise, { 'nodename' : ['volume1', 'volume2', ...], ... }
286 a8083063 Iustin Pop

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

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

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