+ "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT",
+ "ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
+ "FormatError", "SplitNodeOption"
+ ]
+
+
+def _ExtractTagsObject(opts, args):
+ """Extract the tag type object.
+
+ Note that this function will modify its args parameter.
+
+ """
+ if not hasattr(opts, "tag_type"):
+ raise errors.ProgrammerError("tag_type not passed to _ExtractTagsObject")
+ kind = opts.tag_type
+ if kind == constants.TAG_CLUSTER:
+ retval = kind, kind
+ elif kind == constants.TAG_NODE or kind == constants.TAG_INSTANCE:
+ if not args:
+ raise errors.OpPrereqError("no arguments passed to the command")
+ name = args.pop(0)
+ retval = kind, name
+ else:
+ raise errors.ProgrammerError("Unhandled tag type '%s'" % kind)
+ return retval
+
+
+def _ExtendTags(opts, args):
+ """Extend the args if a source file has been given.
+
+ This function will extend the tags with the contents of the file
+ passed in the 'tags_source' attribute of the opts parameter. A file
+ named '-' will be replaced by stdin.
+
+ """
+ fname = opts.tags_source
+ if fname is None:
+ return
+ if fname == "-":
+ new_fh = sys.stdin
+ else:
+ new_fh = open(fname, "r")
+ new_data = []
+ try:
+ # we don't use the nice 'new_data = [line.strip() for line in fh]'
+ # because of python bug 1633941
+ while True:
+ line = new_fh.readline()
+ if not line:
+ break
+ new_data.append(line.strip())
+ finally:
+ new_fh.close()
+ args.extend(new_data)
+
+
+def ListTags(opts, args):
+ """List the tags on a given object.
+
+ This is a generic implementation that knows how to deal with all
+ three cases of tag objects (cluster, node, instance). The opts
+ argument is expected to contain a tag_type field denoting what
+ object type we work on.
+
+ """
+ kind, name = _ExtractTagsObject(opts, args)
+ op = opcodes.OpGetTags(kind=kind, name=name)
+ result = SubmitOpCode(op)
+ result = list(result)
+ result.sort()
+ for tag in result:
+ print tag
+
+
+def AddTags(opts, args):
+ """Add tags on a given object.
+
+ This is a generic implementation that knows how to deal with all
+ three cases of tag objects (cluster, node, instance). The opts
+ argument is expected to contain a tag_type field denoting what
+ object type we work on.
+
+ """
+ kind, name = _ExtractTagsObject(opts, args)
+ _ExtendTags(opts, args)
+ if not args:
+ raise errors.OpPrereqError("No tags to be added")
+ op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
+ SubmitOpCode(op)
+
+
+def RemoveTags(opts, args):
+ """Remove tags from a given object.
+
+ This is a generic implementation that knows how to deal with all
+ three cases of tag objects (cluster, node, instance). The opts
+ argument is expected to contain a tag_type field denoting what
+ object type we work on.
+
+ """
+ kind, name = _ExtractTagsObject(opts, args)
+ _ExtendTags(opts, args)
+ if not args:
+ raise errors.OpPrereqError("No tags to be removed")
+ op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
+ SubmitOpCode(op)
+