Revision ff9c047c lib/objects.py

b/lib/objects.py
147 147
    """Load data from a string."""
148 148
    return ConfigObject.Load(StringIO(data))
149 149

  
150
  def ToDict(self):
151
    """Convert to a dict holding only standard python types.
152

  
153
    The generic routine just dumps all of this object's attributes in
154
    a dict. It does not work if the class has children who are
155
    ConfigObjects themselves (e.g. the nics list in an Instance), in
156
    which case the object should subclass the function in order to
157
    make sure all objects returned are only standard python types.
158

  
159
    """
160
    return dict([(k, getattr(self, k, None)) for k in self.__slots__])
161

  
162
  @classmethod
163
  def FromDict(cls, val):
164
    """Create an object from a dictionary.
165

  
166
    This generic routine takes a dict, instantiates a new instance of
167
    the given class, and sets attributes based on the dict content.
168

  
169
    As for `ToDict`, this does not work if the class has children
170
    who are ConfigObjects themselves (e.g. the nics list in an
171
    Instance), in which case the object should subclass the function
172
    and alter the objects.
173

  
174
    """
175
    if not isinstance(val, dict):
176
      raise errors.ConfigurationError("Invalid object passed to FromDict:"
177
                                      " expected dict, got %s" % type(val))
178
    obj = cls(**val)
179
    return obj
180

  
181
  @staticmethod
182
  def _ContainerToDicts(container):
183
    """Convert the elements of a container to standard python types.
184

  
185
    This method converts a container with elements derived from
186
    ConfigData to standard python types. If the container is a dict,
187
    we don't touch the keys, only the values.
188

  
189
    """
190
    if isinstance(container, dict):
191
      ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
192
    elif isinstance(container, (list, tuple, set, frozenset)):
193
      ret = [elem.ToDict() for elem in container]
194
    else:
195
      raise TypeError("Invalid type %s passed to _ContainerToDicts" %
196
                      type(container))
197
    return ret
198

  
199
  @staticmethod
200
  def _ContainerFromDicts(source, c_type, e_type):
201
    """Convert a container from standard python types.
202

  
203
    This method converts a container with standard python types to
204
    ConfigData objects. If the container is a dict, we don't touch the
205
    keys, only the values.
206

  
207
    """
208
    if not isinstance(c_type, type):
209
      raise TypeError("Container type %s passed to _ContainerFromDicts is"
210
                      " not a type" % type(c_type))
211
    if c_type is dict:
212
      ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
213
    elif c_type in (list, tuple, set, frozenset):
214
      ret = c_type([e_type.FromDict(elem) for elem in source])
215
    else:
216
      raise TypeError("Invalid container type %s passed to"
217
                      " _ContainerFromDicts" % c_type)
218
    return ret
219

  
220
  def __repr__(self):
221
    """Implement __repr__ for ConfigObjects."""
222
    return repr(self.ToDict())
223

  
150 224

  
151 225
class TaggableObject(ConfigObject):
152 226
  """An generic class supporting tags.
......
201 275
    except KeyError:
202 276
      raise errors.TagError("Tag not found")
203 277

  
278
  def ToDict(self):
279
    """Taggable-object-specific conversion to standard python types.
280

  
281
    This replaces the tags set with a list.
282

  
283
    """
284
    bo = super(TaggableObject, self).ToDict()
285

  
286
    tags = bo.get("tags", None)
287
    if isinstance(tags, set):
288
      bo["tags"] = list(tags)
289
    return bo
290

  
291
  @classmethod
292
  def FromDict(cls, val):
293
    """Custom function for instances.
294

  
295
    """
296
    obj = super(TaggableObject, cls).FromDict(val)
297
    if hasattr(obj, "tags") and isinstance(obj.tags, list):
298
      obj.tags = set(obj.tags)
299
    return obj
300

  
204 301

  
205 302
class ConfigData(ConfigObject):
206 303
  """Top-level config object."""
207 304
  __slots__ = ["cluster", "nodes", "instances"]
208 305

  
306
  def ToDict(self):
307
    """Custom function for top-level config data.
308

  
309
    This just replaces the list of instances, nodes and the cluster
310
    with standard python types.
311

  
312
    """
313
    mydict = super(ConfigData, self).ToDict()
314
    mydict["cluster"] = mydict["cluster"].ToDict()
315
    for key in "nodes", "instances":
316
      mydict[key] = self._ContainerToDicts(mydict[key])
317

  
318
    return mydict
319

  
320
  @classmethod
321
  def FromDict(cls, val):
322
    """Custom function for top-level config data
323

  
324
    """
325
    obj = super(ConfigData, cls).FromDict(val)
326
    obj.cluster = Cluster.FromDict(obj.cluster)
327
    obj.nodes = cls._ContainerFromDicts(obj.nodes, dict, Node)
328
    obj.instances = cls._ContainerFromDicts(obj.instances, dict, Instance)
329
    return obj
330

  
209 331

  
210 332
class NIC(ConfigObject):
211 333
  """Config object representing a network card."""
......
286 408
            # be different)
287 409
    return result
288 410

  
411
  def ToDict(self):
412
    """Disk-specific conversion to standard python types.
413

  
414
    This replaces the children lists of objects with lists of
415
    standard python types.
416

  
417
    """
418
    bo = super(Disk, self).ToDict()
419

  
420
    for attr in ("children",):
421
      alist = bo.get(attr, None)
422
      if alist:
423
        bo[attr] = self._ContainerToDicts(alist)
424
    return bo
425

  
426
  @classmethod
427
  def FromDict(cls, val):
428
    """Custom function for Disks
429

  
430
    """
431
    obj = super(Disk, cls).FromDict(val)
432
    if obj.children:
433
      obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
434
    if obj.logical_id and isinstance(obj.logical_id, list):
435
      obj.logical_id = tuple(obj.logical_id)
436
    if obj.physical_id and isinstance(obj.physical_id, list):
437
      obj.physical_id = tuple(obj.physical_id)
438
    return obj
439

  
289 440

  
290 441
class Instance(TaggableObject):
291 442
  """Config object representing an instance."""
......
392 543

  
393 544
    return None
394 545

  
546
  def ToDict(self):
547
    """Instance-specific conversion to standard python types.
548

  
549
    This replaces the children lists of objects with lists of standard
550
    python types.
551

  
552
    """
553
    bo = super(Instance, self).ToDict()
554

  
555
    for attr in "nics", "disks":
556
      alist = bo.get(attr, None)
557
      if alist:
558
        nlist = self._ContainerToDicts(alist)
559
      else:
560
        nlist = []
561
      bo[attr] = nlist
562
    return bo
563

  
564
  @classmethod
565
  def FromDict(cls, val):
566
    """Custom function for instances.
567

  
568
    """
569
    obj = super(Instance, cls).FromDict(val)
570
    obj.nics = cls._ContainerFromDicts(obj.nics, list, NIC)
571
    obj.disks = cls._ContainerFromDicts(obj.disks, list, Disk)
572
    return obj
573

  
395 574

  
396 575
class OS(ConfigObject):
397 576
  """Config object representing an operating system."""

Also available in: Unified diff