Implement tag searching
[ganeti-local] / scripts / gnt-cluster
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 import sys
23 from optparse import make_option
24 import pprint
25
26 from ganeti.cli import *
27 from ganeti import opcodes
28 from ganeti import constants
29
30
31 def InitCluster(opts, args):
32   """Initialize the cluster.
33
34   Args:
35     opts - class with options as members
36     args - list of arguments, expected to be [clustername]
37
38   """
39   op = opcodes.OpInitCluster(cluster_name=args[0],
40                              secondary_ip=opts.secondary_ip,
41                              hypervisor_type=opts.hypervisor_type,
42                              vg_name=opts.vg_name,
43                              mac_prefix=opts.mac_prefix,
44                              def_bridge=opts.def_bridge,
45                              master_netdev=opts.master_netdev)
46   SubmitOpCode(op)
47   return 0
48
49
50 def DestroyCluster(opts, args):
51   """Destroy the cluster.
52
53   Args:
54     opts - class with options as members
55
56   """
57   if not opts.yes_do_it:
58     print ("Destroying a cluster is irreversibly. If you really want destroy"
59            " this cluster, supply the --yes-do-it option.")
60     return 1
61
62   op = opcodes.OpDestroyCluster()
63   SubmitOpCode(op)
64   return 0
65
66
67 def RenameCluster(opts, args):
68   """Rename the cluster.
69
70   Args:
71     opts - class with options as members, we use force only
72     args - list of arguments, expected to be [new_name]
73
74   """
75   name = args[0]
76   if not opts.force:
77     usertext = ("This will rename the cluster to '%s'. If you are connected"
78                 " over the network to the cluster name, the operation is very"
79                 " dangerous as the IP address will be removed from the node"
80                 " and the change may not go through. Continue?") % name
81     if not AskUser(usertext):
82       return 1
83
84   op = opcodes.OpRenameCluster(name=name)
85   SubmitOpCode(op)
86   return 0
87
88
89 def ShowClusterVersion(opts, args):
90   """Write version of ganeti software to the standard output.
91
92   Args:
93     opts - class with options as members
94
95   """
96   op = opcodes.OpQueryClusterInfo()
97   result = SubmitOpCode(op)
98   print ("Software version: %s" % result["software_version"])
99   print ("Internode protocol: %s" % result["protocol_version"])
100   print ("Configuration format: %s" % result["config_version"])
101   print ("OS api version: %s" % result["os_api_version"])
102   print ("Export interface: %s" % result["export_version"])
103   return 0
104
105
106 def ShowClusterMaster(opts, args):
107   """Write name of master node to the standard output.
108
109   Args:
110     opts - class with options as members
111
112   """
113   op = opcodes.OpQueryClusterInfo()
114   result = SubmitOpCode(op)
115   print (result["master"])
116   return 0
117
118
119 def ShowClusterConfig(opts, args):
120   """Shows cluster information.
121
122   """
123   op = opcodes.OpQueryClusterInfo()
124   result = SubmitOpCode(op)
125
126   print ("Cluster name: %s" % result["name"])
127
128   print ("Master node: %s" % result["master"])
129
130   print ("Architecture (this node): %s (%s)" %
131          (result["architecture"][0], result["architecture"][1]))
132
133   return 0
134
135
136 def ClusterCopyFile(opts, args):
137   """Copy a file from master to some nodes.
138
139   Args:
140     opts - class with options as members
141     args - list containing a single element, the file name
142   Opts used:
143     nodes - list containing the name of target nodes; if empty, all nodes
144
145   """
146   op = opcodes.OpClusterCopyFile(filename=args[0], nodes=opts.nodes)
147   SubmitOpCode(op)
148   return 0
149
150
151 def RunClusterCommand(opts, args):
152   """Run a command on some nodes.
153
154   Args:
155     opts - class with options as members
156     args - the command list as a list
157   Opts used:
158     nodes: list containing the name of target nodes; if empty, all nodes
159
160   """
161   command = " ".join(args)
162   nodes = opts.nodes
163   op = opcodes.OpRunClusterCommand(command=command, nodes=nodes)
164   result = SubmitOpCode(op)
165   for node, output, exit_code in result:
166     print ("------------------------------------------------")
167     print ("node: %s" % node)
168     print ("%s" % output)
169     print ("return code = %s" % exit_code)
170
171
172 def VerifyCluster(opts, args):
173   """Verify integrity of cluster, performing various test on nodes.
174
175   Args:
176     opts - class with options as members
177
178   """
179   op = opcodes.OpVerifyCluster()
180   result = SubmitOpCode(op)
181   return result
182
183
184 def MasterFailover(opts, args):
185   """Failover the master node.
186
187   This command, when run on a non-master node, will cause the current
188   master to cease being master, and the non-master to become new
189   master.
190
191   """
192   op = opcodes.OpMasterFailover()
193   SubmitOpCode(op)
194
195
196 def SearchTags(opts, args):
197   """Searches the tags on all the cluster.
198
199   """
200   op = opcodes.OpSearchTags(pattern=args[0])
201   result = SubmitOpCode(op)
202   if not result:
203     return 1
204   result = list(result)
205   result.sort()
206   for path, tag in result:
207     print "%s %s" % (path, tag)
208
209
210 # this is an option common to more than one command, so we declare
211 # it here and reuse it
212 node_option = make_option("-n", "--node", action="append", dest="nodes",
213                           help="Node to copy to (if not given, all nodes)"
214                           ", can be given multiple times", metavar="<node>",
215                           default=[])
216
217 commands = {
218   'init': (InitCluster, ARGS_ONE,
219            [DEBUG_OPT,
220             make_option("-s", "--secondary-ip", dest="secondary_ip",
221                         help="Specify the secondary ip for this node;"
222                         " if given, the entire cluster must have secondary"
223                         " addresses",
224                         metavar="ADDRESS", default=None),
225             make_option("-t", "--hypervisor-type", dest="hypervisor_type",
226                         help="Specify the hypervisor type (xen-3.0, fake)",
227                         metavar="TYPE", choices=["xen-3.0", "fake"],
228                         default="xen-3.0",),
229             make_option("-m", "--mac-prefix", dest="mac_prefix",
230                         help="Specify the mac prefix for the instance IP"
231                         " addresses, in the format XX:XX:XX",
232                         metavar="PREFIX",
233                         default="aa:00:00",),
234             make_option("-g", "--vg-name", dest="vg_name",
235                         help="Specify the volume group name "
236                         " (cluster-wide) for disk allocation [xenvg]",
237                         metavar="VG",
238                         default="xenvg",),
239             make_option("-b", "--bridge", dest="def_bridge",
240                         help="Specify the default bridge name (cluster-wide)"
241                           " to connect the instances to [%s]" %
242                           constants.DEFAULT_BRIDGE,
243                         metavar="BRIDGE",
244                         default=constants.DEFAULT_BRIDGE,),
245             make_option("--master-netdev", dest="master_netdev",
246                         help="Specify the node interface (cluster-wide)"
247                           " on which the master IP address will be added "
248                           " [%s]" % constants.DEFAULT_BRIDGE,
249                         metavar="NETDEV",
250                         default=constants.DEFAULT_BRIDGE,),
251             ],
252            "[opts...] <cluster_name>",
253            "Initialises a new cluster configuration"),
254   'destroy': (DestroyCluster, ARGS_NONE,
255               [DEBUG_OPT,
256                make_option("--yes-do-it", dest="yes_do_it",
257                            help="Destroy cluster",
258                            action="store_true"),
259               ],
260               "", "Destroy cluster"),
261   'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
262                "<new_name>",
263                "Renames the cluster"),
264   'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT],
265              "", "Does a check on the cluster configuration"),
266   'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
267                      "", "Makes the current node the master"),
268   'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
269               "", "Shows the cluster version"),
270   'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
271                 "", "Shows the cluster master"),
272   'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
273                "[-n node...] <filename>",
274                "Copies a file to all (or only some) nodes"),
275   'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
276               "[-n node...] <command>",
277               "Runs a command on all (or only some) nodes"),
278   'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
279                  "", "Show cluster configuration"),
280   'list-tags': (ListTags, ARGS_NONE,
281                 [DEBUG_OPT], "", "List the tags of the cluster"),
282   'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
283                "tag...", "Add tags to the cluster"),
284   'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
285                   "tag...", "Remove tags from the cluster"),
286   'search-tags': (SearchTags, ARGS_ONE,
287                   [DEBUG_OPT], "", "Searches the tags on all objects on"
288                   " the cluster for a given pattern (regex)"),
289   }
290
291 if __name__ == '__main__':
292   sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}))