Revision 319856a9 lib/objects.py

b/lib/objects.py
27 27
"""
28 28

  
29 29

  
30
import cPickle
30
import simplejson
31 31
from cStringIO import StringIO
32 32
import ConfigParser
33 33
import re
......
56 56
  __slots__ = []
57 57

  
58 58
  def __init__(self, **kwargs):
59
    for i in kwargs:
60
      setattr(self, i, kwargs[i])
59
    for k, v in kwargs.iteritems():
60
      setattr(self, k, v)
61 61

  
62 62
  def __getattr__(self, name):
63 63
    if name not in self.__slots__:
......
82 82
      if name in self.__slots__:
83 83
        setattr(self, name, state[name])
84 84

  
85
  @staticmethod
86
  def FindGlobal(module, name):
87
    """Function filtering the allowed classes to be un-pickled.
88

  
89
    Currently, we only allow the classes from this module which are
90
    derived from ConfigObject.
91

  
92
    """
93
    # Also support the old module name (ganeti.config)
94
    cls = None
95
    if module == "ganeti.config" or module == "ganeti.objects":
96
      if name == "ConfigData":
97
        cls = ConfigData
98
      elif name == "NIC":
99
        cls = NIC
100
      elif name == "Disk" or name == "BlockDev":
101
        cls = Disk
102
      elif name == "Instance":
103
        cls = Instance
104
      elif name == "OS":
105
        cls = OS
106
      elif name == "Node":
107
        cls = Node
108
      elif name == "Cluster":
109
        cls = Cluster
110
    elif module == "__builtin__":
111
      if name == "set":
112
        cls = set
113
    if cls is None:
114
      raise cPickle.UnpicklingError("Class %s.%s not allowed due to"
115
                                    " security concerns" % (module, name))
116
    return cls
117

  
118 85
  def Dump(self, fobj):
119
    """Dump this instance to a file object.
120

  
121
    Note that we use the HIGHEST_PROTOCOL, as it brings benefits for
122
    the new classes.
86
    """Dump to a file object.
123 87

  
124 88
    """
125
    dumper = cPickle.Pickler(fobj, cPickle.HIGHEST_PROTOCOL)
126
    dumper.dump(self)
89
    simplejson.dump(self.ToDict(), fobj)
127 90

  
128
  @staticmethod
129
  def Load(fobj):
130
    """Unpickle data from the given stream.
131

  
132
    This uses the `FindGlobal` function to filter the allowed classes.
91
  @classmethod
92
  def Load(cls, fobj):
93
    """Load data from the given stream.
133 94

  
134 95
    """
135
    loader = cPickle.Unpickler(fobj)
136
    loader.find_global = ConfigObject.FindGlobal
137
    return loader.load()
96
    return cls.FromDict(simplejson.load(fobj))
138 97

  
139 98
  def Dumps(self):
140
    """Dump this instance and return the string representation."""
99
    """Dump and return the string representation."""
141 100
    buf = StringIO()
142 101
    self.Dump(buf)
143 102
    return buf.getvalue()
144 103

  
145
  @staticmethod
146
  def Loads(data):
104
  @classmethod
105
  def Loads(cls, data):
147 106
    """Load data from a string."""
148
    return ConfigObject.Load(StringIO(data))
107
    return cls.Load(StringIO(data))
149 108

  
150 109
  def ToDict(self):
151 110
    """Convert to a dict holding only standard python types.
......
175 134
    if not isinstance(val, dict):
176 135
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
177 136
                                      " expected dict, got %s" % type(val))
178
    obj = cls(**val)
137
    val_str = dict([(str(k), v) for k, v in val.iteritems()])
138
    obj = cls(**val_str)
179 139
    return obj
180 140

  
181 141
  @staticmethod
......
239 199
    if not isinstance(tag, basestring):
240 200
      raise errors.TagError("Invalid tag type (not a string)")
241 201
    if len(tag) > constants.MAX_TAG_LEN:
242
      raise errors.TagError("Tag too long (>%d)" % constants.MAX_TAG_LEN)
202
      raise errors.TagError("Tag too long (>%d characters)" %
203
                            constants.MAX_TAG_LEN)
243 204
    if not tag:
244 205
      raise errors.TagError("Tags cannot be empty")
245 206
    if not re.match("^[ \w.+*/:-]+$", tag):
......
607 568
    "default_bridge",
608 569
    ]
609 570

  
571
  def ToDict(self):
572
    """Custom function for cluster.
573

  
574
    """
575
    mydict = super(TaggableObject, self).ToDict()
576
    mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
577
    return mydict
578

  
579
  @classmethod
580
  def FromDict(cls, val):
581
    """Custom function for cluster.
582

  
583
    """
584
    obj = super(TaggableObject, cls).FromDict(val)
585
    if not isinstance(obj.tcpudp_port_pool, set):
586
      obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
587
    return obj
588

  
610 589

  
611 590
class SerializableConfigParser(ConfigParser.SafeConfigParser):
612 591
  """Simple wrapper over ConfigParse that allows serialization.

Also available in: Unified diff