Revision 5c947f38 lib/objects.py

b/lib/objects.py
30 30
import cPickle
31 31
from cStringIO import StringIO
32 32
import ConfigParser
33
import re
33 34

  
34 35
from ganeti import errors
36
from ganeti import constants
35 37

  
36 38

  
37 39
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
......
146 148
    return ConfigObject.Load(StringIO(data))
147 149

  
148 150

  
151
class TaggableObject(object):
152
  """An generic class supporting tags.
153

  
154
  """
155
  @staticmethod
156
  def ValidateTag(tag):
157
    """Check if a tag is valid.
158

  
159
    If the tag is invalid, an errors.TagError will be raised. The
160
    function has no return value.
161

  
162
    """
163
    if not isinstance(tag, basestring):
164
      raise errors.TagError, ("Invalid tag type (not a string)")
165
    if len(tag) > constants.MAX_TAG_LEN:
166
      raise errors.TagError, ("Tag too long (>%d)" %
167
                              constants.MAX_TAG_LEN)
168
    if not tag:
169
      raise errors.TagError, ("Tags cannot be empty")
170
    if not re.match("^[ \w.+*/:-]+$", tag):
171
      raise errors.TagError, ("Tag contains invalid characters")
172

  
173
  def GetTags(self):
174
    """Return the tags list.
175

  
176
    """
177
    tags = getattr(self, "tags", None)
178
    if tags is None:
179
      tags = self.tags = set()
180
    return tags
181

  
182
  def AddTag(self, tag):
183
    """Add a new tag.
184

  
185
    """
186
    self.ValidateTag(tag)
187
    tags = self.GetTags()
188
    if len(tags) >= constants.MAX_TAGS_PER_OBJ:
189
      raise errors.TagError, ("Too many tags")
190
    self.GetTags().add(tag)
191

  
192
  def RemoveTag(self, tag):
193
    """Remove a tag.
194

  
195
    """
196
    self.ValidateTag(tag)
197
    tags = self.GetTags()
198
    try:
199
      tags.remove(tag)
200
    except KeyError:
201
      raise errors.TagError, ("Tag not found")
202

  
203

  
149 204
class ConfigData(ConfigObject):
150 205
  """Top-level config object."""
151 206
  __slots__ = ["cluster", "nodes", "instances"]
......
231 286
    return result
232 287

  
233 288

  
234
class Instance(ConfigObject):
289
class Instance(ConfigObject, TaggableObject):
235 290
  """Config object representing an instance."""
236 291
  __slots__ = [
237 292
    "name",
......
243 298
    "nics",
244 299
    "disks",
245 300
    "disk_template",
301
    "tags",
246 302
    ]
247 303

  
248 304
  def _ComputeSecondaryNodes(self):
......
337 393
    ]
338 394

  
339 395

  
340
class Node(ConfigObject):
396
class Node(ConfigObject, TaggableObject):
341 397
  """Config object representing a node."""
342
  __slots__ = ["name", "primary_ip", "secondary_ip"]
398
  __slots__ = ["name", "primary_ip", "secondary_ip", "tags"]
343 399

  
344 400

  
345
class Cluster(ConfigObject):
401
class Cluster(ConfigObject, TaggableObject):
346 402
  """Config object representing the cluster."""
347 403
  __slots__ = [
348 404
    "config_version",
......
353 409
    "mac_prefix",
354 410
    "volume_group_name",
355 411
    "default_bridge",
412
    "tags",
356 413
    ]
357 414

  
415

  
358 416
class SerializableConfigParser(ConfigParser.SafeConfigParser):
359 417
  """Simple wrapper over ConfigParse that allows serialization.
360 418

  

Also available in: Unified diff