diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 829c019139778d9f0059d2e2ecd58ce8d8a26f7c..5864ee1cf33755b034799dd48140474ca452a78b 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -4141,6 +4141,42 @@ class LUGetTags(TagsLU): return self.target.GetTags() +class LUSearchTags(NoHooksLU): + """Searches the tags for a given pattern. + + """ + _OP_REQP = ["pattern"] + + def CheckPrereq(self): + """Check prerequisites. + + This checks the pattern passed for validity by compiling it. + + """ + try: + self.re = re.compile(self.op.pattern) + except re.error, err: + raise errors.OpPrereqError("Invalid search pattern '%s': %s" % + (self.op.pattern, err)) + + def Exec(self, feedback_fn): + """Returns the tag list. + + """ + cfg = self.cfg + tgts = [("/cluster", cfg.GetClusterInfo())] + ilist = [cfg.GetInstanceInfo(name) for name in cfg.GetInstanceList()] + tgts.extend([("/instances/%s" % i.name, i) for i in ilist]) + nlist = [cfg.GetNodeInfo(name) for name in cfg.GetNodeList()] + tgts.extend([("/nodes/%s" % n.name, n) for n in nlist]) + results = [] + for path, target in tgts: + for tag in target.GetTags(): + if self.re.search(tag): + results.append((path, tag)) + return results + + class LUAddTags(TagsLU): """Sets a tag on a given object. diff --git a/lib/mcpu.py b/lib/mcpu.py index 139a3bd6887eaffadf0c89c22ee661795fe4ad8d..5226fd7ffe5549535339a1bb599cc2da84e7b435 100644 --- a/lib/mcpu.py +++ b/lib/mcpu.py @@ -80,6 +80,7 @@ class Processor(object): opcodes.OpExportInstance: cmdlib.LUExportInstance, # tags lu opcodes.OpGetTags: cmdlib.LUGetTags, + opcodes.OpSearchTags: cmdlib.LUSearchTags, opcodes.OpAddTags: cmdlib.LUAddTags, opcodes.OpDelTags: cmdlib.LUDelTags, } diff --git a/lib/opcodes.py b/lib/opcodes.py index 43cefaeea942cb28495fbcd3e4ffd2fdb54af5e9..99f44392c97da0e4dc12d469c34f41c42dfef503 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -263,6 +263,12 @@ class OpGetTags(OpCode): __slots__ = ["kind", "name"] +class OpSearchTags(OpCode): + """Searches the tags in the cluster for a given pattern.""" + OP_ID = "OP_TAGS_SEARCH" + __slots__ = ["pattern"] + + class OpAddTags(OpCode): """Add a list of tags on a given object.""" OP_ID = "OP_TAGS_SET" diff --git a/man/gnt-cluster.sgml b/man/gnt-cluster.sgml index 5781c0949672e82afc57370c35e88b1b06cf330a..4f5560297d35086c21c824adfed9c503a51c9aee 100644 --- a/man/gnt-cluster.sgml +++ b/man/gnt-cluster.sgml @@ -301,6 +301,42 @@ </para> </refsect2> + <refsect2> + <title>SEARCH-TAGS</title> + + <cmdsynopsis> + <command>search-tags</command> + <arg choice="req"><replaceable>pattern</replaceable></arg> + </cmdsynopsis> + + <para> + Searches the tags on all objects in the cluster (the cluster + itself, the nodes and the instances) for a given pattern. The + pattern is interpreted as a regular expression and a search + will be done on it (i.e. the given pattern is not anchored to + the beggining of the string; if you want that, prefix the + pattern with <literal>^</literal>). + </para> + + <para> + If no tags are matching the pattern, the exit code of the + command will be one. If there is at least one match, the exit + code will be zero. Each match is listed on one line, the + object and the tag separated by a space. The cluster will be + listed as <filename>/cluster</filename>, a node will be listed + as + <filename>/nodes/<replaceable>name</replaceable></filename>, + and an instance as + <filename>/instances/<replaceable>name</replaceable></filename>. + Example: + </para> +<screen> +# gnt-cluster search time +/cluster ctime:2007-09-01 +/nodes/node1.example.com mtime:2007-10-04 +</screen> + </refsect2> + <refsect2> <title>VERIFY</title> diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster index 9be9904a84c8605241aa58655cd204b6d7bce685..42451a59f9f9e1ea3a7745e727424b87896d673b 100755 --- a/scripts/gnt-cluster +++ b/scripts/gnt-cluster @@ -193,6 +193,20 @@ def MasterFailover(opts, args): SubmitOpCode(op) +def SearchTags(opts, args): + """Searches the tags on all the cluster. + + """ + op = opcodes.OpSearchTags(pattern=args[0]) + result = SubmitOpCode(op) + if not result: + return 1 + result = list(result) + result.sort() + for path, tag in result: + print "%s %s" % (path, tag) + + # this is an option common to more than one command, so we declare # it here and reuse it node_option = make_option("-n", "--node", action="append", dest="nodes", @@ -269,6 +283,9 @@ commands = { "tag...", "Add tags to the cluster"), 'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT], "tag...", "Remove tags from the cluster"), + 'search-tags': (SearchTags, ARGS_ONE, + [DEBUG_OPT], "", "Searches the tags on all objects on" + " the cluster for a given pattern (regex)"), } if __name__ == '__main__':