Commit 5c947f38 authored by Iustin Pop's avatar Iustin Pop

Implement tag support for cluster, nodes and instances.

This is only the backend part, from the command line the tags can't be
read/modified yet.

Reviewed-by: imsnah
parent 37d19eb2
......@@ -3506,3 +3506,108 @@ class LUExportInstance(LogicalUnit):
if not rpc.call_export_remove(node,
logger.Error("could not remove older export for instance %s"
" on node %s" % (, node))
class TagsLU(NoHooksLU):
"""Generic tags LU.
This is an abstract class which is the parent of all the other tags LUs.
def CheckPrereq(self):
"""Check prerequisites.
if self.op.kind == constants.TAG_CLUSTER: = self.cfg.GetClusterInfo()
elif self.op.kind == constants.TAG_NODE:
name = self.cfg.ExpandNodeName(
if name is None:
raise errors.OpPrereqError, ("Invalid node name (%s)" %
(,)) = name = self.cfg.GetNodeInfo(name)
elif self.op.kind == constants.TAG_INSTANCE:
name = self.cfg.ExpandInstanceName(name)
if name is None:
raise errors.OpPrereqError, ("Invalid instance name (%s)" %
(,)) = name = self.cfg.GetInstanceInfo(name)
raise errors.OpPrereqError, ("Wrong tag type requested (%s)" %
class LUGetTags(TagsLU):
"""Returns the tags of a given object.
_OP_REQP = ["kind", "name"]
def Exec(self, feedback_fn):
"""Returns the tag list.
class LUAddTag(TagsLU):
"""Sets a tag on a given object.
_OP_REQP = ["kind", "name", "tag"]
def CheckPrereq(self):
"""Check prerequisites.
This checks the type and length of the tag name and value.
def Exec(self, feedback_fn):
"""Sets the tag.
except errors.TagError, err:
raise errors.OpExecError, ("Error while setting tag: %s" % str(err))
except errors.ConfigurationError:
raise errors.OpRetryError, ("There has been a modification to the"
" config file and the operation has been"
" aborted. Please retry.")
class LUDelTag(TagsLU):
"""Delete a tag from a given object.
_OP_REQP = ["kind", "name", "tag"]
def CheckPrereq(self):
"""Check prerequisites.
This checks that we have the given tag.
if self.op.tag not in
raise errors.OpPrereqError, ("Tag not found")
def Exec(self, feedback_fn):
"""Remove the tag from the object.
except errors.ConfigurationError:
raise errors.OpRetryError, ("There has been a modification to the"
" config file and the operation has been"
" aborted. Please retry.")
......@@ -85,6 +85,13 @@ INISECT_INS = "instance"
# common exit codes
# tags
TAG_CLUSTER = "cluster"
TAG_NODE = "node"
TAG_INSTANCE = "instance"
# others
DEFAULT_BRIDGE = "xen-br0"
SYNC_SPEED = 30 * 1024
......@@ -136,6 +136,12 @@ class OpExecError(GenericError):
class OpRetryError(OpExecError):
"""Error during OpCode execution, action can be retried.
class OpCodeUnknown(GenericError):
"""Unknown opcode submitted.
......@@ -173,3 +179,11 @@ class UnitParseError(GenericError):
class SshKeyError(GenericError):
"""Invalid SSH key.
class TagError(GenericError):
"""Generic tag error.
The argument to this exception will show the exact error.
......@@ -82,6 +82,10 @@ class Processor(object):
# exports lu
opcodes.OpQueryExports: cmdlib.LUQueryExports,
opcodes.OpExportInstance: cmdlib.LUExportInstance,
# tags lu
opcodes.OpGetTags: cmdlib.LUGetTags,
opcodes.OpSetTag: cmdlib.LUAddTag,
opcodes.OpDelTag: cmdlib.LUDelTag,
......@@ -30,8 +30,10 @@ pass to and from external parties.
import cPickle
from cStringIO import StringIO
import ConfigParser
import re
from ganeti import errors
from ganeti import constants
__all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
......@@ -146,6 +148,59 @@ class ConfigObject(object):
return ConfigObject.Load(StringIO(data))
class TaggableObject(object):
"""An generic class supporting tags.
def ValidateTag(tag):
"""Check if a tag is valid.
If the tag is invalid, an errors.TagError will be raised. The
function has no return value.
if not isinstance(tag, basestring):
raise errors.TagError, ("Invalid tag type (not a string)")
if len(tag) > constants.MAX_TAG_LEN:
raise errors.TagError, ("Tag too long (>%d)" %
if not tag:
raise errors.TagError, ("Tags cannot be empty")
if not re.match("^[ \w.+*/:-]+$", tag):
raise errors.TagError, ("Tag contains invalid characters")
def GetTags(self):
"""Return the tags list.
tags = getattr(self, "tags", None)
if tags is None:
tags = self.tags = set()
return tags
def AddTag(self, tag):
"""Add a new tag.
tags = self.GetTags()
if len(tags) >= constants.MAX_TAGS_PER_OBJ:
raise errors.TagError, ("Too many tags")
def RemoveTag(self, tag):
"""Remove a tag.
tags = self.GetTags()
except KeyError:
raise errors.TagError, ("Tag not found")
class ConfigData(ConfigObject):
"""Top-level config object."""
__slots__ = ["cluster", "nodes", "instances"]
......@@ -231,7 +286,7 @@ class Disk(ConfigObject):
return result
class Instance(ConfigObject):
class Instance(ConfigObject, TaggableObject):
"""Config object representing an instance."""
__slots__ = [
......@@ -243,6 +298,7 @@ class Instance(ConfigObject):
def _ComputeSecondaryNodes(self):
......@@ -337,12 +393,12 @@ class OS(ConfigObject):
class Node(ConfigObject):
class Node(ConfigObject, TaggableObject):
"""Config object representing a node."""
__slots__ = ["name", "primary_ip", "secondary_ip"]
__slots__ = ["name", "primary_ip", "secondary_ip", "tags"]
class Cluster(ConfigObject):
class Cluster(ConfigObject, TaggableObject):
"""Config object representing the cluster."""
__slots__ = [
......@@ -353,8 +409,10 @@ class Cluster(ConfigObject):
class SerializableConfigParser(ConfigParser.SafeConfigParser):
"""Simple wrapper over ConfigParse that allows serialization.
......@@ -239,3 +239,22 @@ class OpExportInstance(OpCode):
"""Export an instance."""
__slots__ = ["instance_name", "target_node", "shutdown"]
# Tags opcodes
class OpGetTags(OpCode):
"""Returns the tags of the given object."""
__slots__ = ["kind", "name"]
class OpSetTag(OpCode):
"""Sets the value of a tag on a given object."""
__slots__ = ["kind", "name", "tag"]
class OpDelTag(OpCode):
"""Remove a tag from a given object."""
__slots__ = ["kind", "name", "tag"]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment