Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 1094acda

History | View | Annotate | Download (23.9 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 2f79bd34 Iustin Pop
# pylint: disable-msg=W0401,W0614
23 2f79bd34 Iustin Pop
# W0401: Wildcard import ganeti.cli
24 2f79bd34 Iustin Pop
# W0614: Unused import %s from wildcard import (since we need cli)
25 2f79bd34 Iustin Pop
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
from optparse import make_option
28 b3989551 Iustin Pop
import os.path
29 a8083063 Iustin Pop
30 a8083063 Iustin Pop
from ganeti.cli import *
31 a8083063 Iustin Pop
from ganeti import opcodes
32 c2a62a33 Michael Hanselmann
from ganeti import constants
33 f4d4e184 Iustin Pop
from ganeti import errors
34 b63ed789 Iustin Pop
from ganeti import utils
35 a0c9f010 Michael Hanselmann
from ganeti import bootstrap
36 b3989551 Iustin Pop
from ganeti import ssh
37 d3cfe525 Guido Trotter
from ganeti import objects
38 a8083063 Iustin Pop
39 a8083063 Iustin Pop
40 4331f6cd Michael Hanselmann
@UsesRPC
41 a8083063 Iustin Pop
def InitCluster(opts, args):
42 a8083063 Iustin Pop
  """Initialize the cluster.
43 a8083063 Iustin Pop
44 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
45 469ee405 Iustin Pop
  @type args: list
46 469ee405 Iustin Pop
  @param args: should contain only one element, the desired
47 469ee405 Iustin Pop
      cluster name
48 469ee405 Iustin Pop
  @rtype: int
49 469ee405 Iustin Pop
  @return: the desired exit code
50 a8083063 Iustin Pop
51 a8083063 Iustin Pop
  """
52 90b6aa3a Manuel Franceschini
  if not opts.lvm_storage and opts.vg_name:
53 3a24c527 Iustin Pop
    ToStderr("Options --no-lvm-storage and --vg-name conflict.")
54 90b6aa3a Manuel Franceschini
    return 1
55 90b6aa3a Manuel Franceschini
56 90b6aa3a Manuel Franceschini
  vg_name = opts.vg_name
57 90b6aa3a Manuel Franceschini
  if opts.lvm_storage and not opts.vg_name:
58 90b6aa3a Manuel Franceschini
    vg_name = constants.DEFAULT_VG
59 90b6aa3a Manuel Franceschini
60 ea3a925f Alexander Schreiber
  hvlist = opts.enabled_hypervisors
61 ea3a925f Alexander Schreiber
  if hvlist is not None:
62 ea3a925f Alexander Schreiber
    hvlist = hvlist.split(",")
63 ea3a925f Alexander Schreiber
  else:
64 8f348e36 Guido Trotter
    hvlist = [opts.default_hypervisor]
65 02691904 Alexander Schreiber
66 02691904 Alexander Schreiber
  # avoid an impossible situation
67 8f348e36 Guido Trotter
  if opts.default_hypervisor not in hvlist:
68 8f348e36 Guido Trotter
    ToStderr("The default hypervisor requested (%s) is not"
69 8f348e36 Guido Trotter
             " within the enabled hypervisor list (%s)" %
70 8f348e36 Guido Trotter
             (opts.default_hypervisor, hvlist))
71 8f348e36 Guido Trotter
    return 1
72 ea3a925f Alexander Schreiber
73 f8e7ddca Guido Trotter
  hvparams = dict(opts.hvparams)
74 ea3a925f Alexander Schreiber
  beparams = opts.beparams
75 b6a30b0d Guido Trotter
  nicparams = opts.nicparams
76 ea3a925f Alexander Schreiber
77 ea3a925f Alexander Schreiber
  # prepare beparams dict
78 d3cfe525 Guido Trotter
  beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams)
79 a5728081 Guido Trotter
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
80 ea3a925f Alexander Schreiber
81 b6a30b0d Guido Trotter
  # prepare nicparams dict
82 b6a30b0d Guido Trotter
  nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams)
83 b6a30b0d Guido Trotter
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
84 b6a30b0d Guido Trotter
85 ea3a925f Alexander Schreiber
  # prepare hvparams dict
86 ea3a925f Alexander Schreiber
  for hv in constants.HYPER_TYPES:
87 ea3a925f Alexander Schreiber
    if hv not in hvparams:
88 ea3a925f Alexander Schreiber
      hvparams[hv] = {}
89 d3cfe525 Guido Trotter
    hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv])
90 a5728081 Guido Trotter
    utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
91 ea3a925f Alexander Schreiber
92 ea3a925f Alexander Schreiber
  for hv in hvlist:
93 ea3a925f Alexander Schreiber
    if hv not in constants.HYPER_TYPES:
94 1f9611b1 Alexander Schreiber
      ToStderr("invalid hypervisor: %s", hv)
95 ea3a925f Alexander Schreiber
      return 1
96 ea3a925f Alexander Schreiber
97 a0c9f010 Michael Hanselmann
  bootstrap.InitCluster(cluster_name=args[0],
98 a0c9f010 Michael Hanselmann
                        secondary_ip=opts.secondary_ip,
99 a0c9f010 Michael Hanselmann
                        vg_name=vg_name,
100 a0c9f010 Michael Hanselmann
                        mac_prefix=opts.mac_prefix,
101 a0c9f010 Michael Hanselmann
                        def_bridge=opts.def_bridge,
102 a0c9f010 Michael Hanselmann
                        master_netdev=opts.master_netdev,
103 ea3a925f Alexander Schreiber
                        file_storage_dir=opts.file_storage_dir,
104 ea3a925f Alexander Schreiber
                        enabled_hypervisors=hvlist,
105 8f348e36 Guido Trotter
                        default_hypervisor=opts.default_hypervisor,
106 ea3a925f Alexander Schreiber
                        hvparams=hvparams,
107 ce735215 Guido Trotter
                        beparams=beparams,
108 b6a30b0d Guido Trotter
                        nicparams=nicparams,
109 ce735215 Guido Trotter
                        candidate_pool_size=opts.candidate_pool_size,
110 b86a6bcd Guido Trotter
                        modify_etc_hosts=opts.modify_etc_hosts,
111 ce735215 Guido Trotter
                        )
112 a8083063 Iustin Pop
  return 0
113 a8083063 Iustin Pop
114 a8083063 Iustin Pop
115 4331f6cd Michael Hanselmann
@UsesRPC
116 a8083063 Iustin Pop
def DestroyCluster(opts, args):
117 a8083063 Iustin Pop
  """Destroy the cluster.
118 a8083063 Iustin Pop
119 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
120 469ee405 Iustin Pop
  @type args: list
121 469ee405 Iustin Pop
  @param args: should be an empty list
122 469ee405 Iustin Pop
  @rtype: int
123 469ee405 Iustin Pop
  @return: the desired exit code
124 098c0958 Michael Hanselmann
125 a8083063 Iustin Pop
  """
126 a8083063 Iustin Pop
  if not opts.yes_do_it:
127 3a24c527 Iustin Pop
    ToStderr("Destroying a cluster is irreversible. If you really want"
128 3a24c527 Iustin Pop
             " destroy this cluster, supply the --yes-do-it option.")
129 a8083063 Iustin Pop
    return 1
130 a8083063 Iustin Pop
131 a8083063 Iustin Pop
  op = opcodes.OpDestroyCluster()
132 140aa4a8 Iustin Pop
  master = SubmitOpCode(op)
133 140aa4a8 Iustin Pop
  # if we reached this, the opcode didn't fail; we can proceed to
134 140aa4a8 Iustin Pop
  # shutdown all the daemons
135 140aa4a8 Iustin Pop
  bootstrap.FinalizeClusterDestroy(master)
136 a8083063 Iustin Pop
  return 0
137 a8083063 Iustin Pop
138 a8083063 Iustin Pop
139 07bd8a51 Iustin Pop
def RenameCluster(opts, args):
140 07bd8a51 Iustin Pop
  """Rename the cluster.
141 07bd8a51 Iustin Pop
142 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
143 469ee405 Iustin Pop
  @type args: list
144 469ee405 Iustin Pop
  @param args: should contain only one element, the new cluster name
145 469ee405 Iustin Pop
  @rtype: int
146 469ee405 Iustin Pop
  @return: the desired exit code
147 07bd8a51 Iustin Pop
148 07bd8a51 Iustin Pop
  """
149 07bd8a51 Iustin Pop
  name = args[0]
150 07bd8a51 Iustin Pop
  if not opts.force:
151 07bd8a51 Iustin Pop
    usertext = ("This will rename the cluster to '%s'. If you are connected"
152 07bd8a51 Iustin Pop
                " over the network to the cluster name, the operation is very"
153 07bd8a51 Iustin Pop
                " dangerous as the IP address will be removed from the node"
154 07bd8a51 Iustin Pop
                " and the change may not go through. Continue?") % name
155 47988778 Iustin Pop
    if not AskUser(usertext):
156 07bd8a51 Iustin Pop
      return 1
157 07bd8a51 Iustin Pop
158 07bd8a51 Iustin Pop
  op = opcodes.OpRenameCluster(name=name)
159 07bd8a51 Iustin Pop
  SubmitOpCode(op)
160 07bd8a51 Iustin Pop
  return 0
161 07bd8a51 Iustin Pop
162 07bd8a51 Iustin Pop
163 afee0879 Iustin Pop
def RedistributeConfig(opts, args):
164 afee0879 Iustin Pop
  """Forces push of the cluster configuration.
165 afee0879 Iustin Pop
166 afee0879 Iustin Pop
  @param opts: the command line options selected by the user
167 afee0879 Iustin Pop
  @type args: list
168 afee0879 Iustin Pop
  @param args: empty list
169 afee0879 Iustin Pop
  @rtype: int
170 afee0879 Iustin Pop
  @return: the desired exit code
171 afee0879 Iustin Pop
172 afee0879 Iustin Pop
  """
173 81a49123 Iustin Pop
  op = opcodes.OpRedistributeConfig()
174 afee0879 Iustin Pop
  SubmitOrSend(op, opts)
175 afee0879 Iustin Pop
  return 0
176 afee0879 Iustin Pop
177 afee0879 Iustin Pop
178 a8083063 Iustin Pop
def ShowClusterVersion(opts, args):
179 a8083063 Iustin Pop
  """Write version of ganeti software to the standard output.
180 a8083063 Iustin Pop
181 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
182 469ee405 Iustin Pop
  @type args: list
183 469ee405 Iustin Pop
  @param args: should be an empty list
184 469ee405 Iustin Pop
  @rtype: int
185 469ee405 Iustin Pop
  @return: the desired exit code
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
  """
188 2e7b8369 Iustin Pop
  cl = GetClient()
189 2e7b8369 Iustin Pop
  result = cl.QueryClusterInfo()
190 3a24c527 Iustin Pop
  ToStdout("Software version: %s", result["software_version"])
191 3a24c527 Iustin Pop
  ToStdout("Internode protocol: %s", result["protocol_version"])
192 3a24c527 Iustin Pop
  ToStdout("Configuration format: %s", result["config_version"])
193 3a24c527 Iustin Pop
  ToStdout("OS api version: %s", result["os_api_version"])
194 3a24c527 Iustin Pop
  ToStdout("Export interface: %s", result["export_version"])
195 a8083063 Iustin Pop
  return 0
196 a8083063 Iustin Pop
197 a8083063 Iustin Pop
198 a8083063 Iustin Pop
def ShowClusterMaster(opts, args):
199 a8083063 Iustin Pop
  """Write name of master node to the standard output.
200 a8083063 Iustin Pop
201 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
202 469ee405 Iustin Pop
  @type args: list
203 469ee405 Iustin Pop
  @param args: should be an empty list
204 469ee405 Iustin Pop
  @rtype: int
205 469ee405 Iustin Pop
  @return: the desired exit code
206 a8083063 Iustin Pop
207 a8083063 Iustin Pop
  """
208 8eb148ae Iustin Pop
  master = bootstrap.GetMaster()
209 8eb148ae Iustin Pop
  ToStdout(master)
210 a8083063 Iustin Pop
  return 0
211 a8083063 Iustin Pop
212 1094acda Guido Trotter
def _PrintGroupedParams(paramsdict):
213 1094acda Guido Trotter
  """Print Grouped parameters (be, nic, disk) by group.
214 1094acda Guido Trotter
215 1094acda Guido Trotter
  @type paramsdict: dict of dicts
216 1094acda Guido Trotter
  @param paramsdict: {group: {param: value, ...}, ...}
217 1094acda Guido Trotter
218 1094acda Guido Trotter
  """
219 1094acda Guido Trotter
  for gr_name, gr_dict in paramsdict.items():
220 1094acda Guido Trotter
    ToStdout("  - %s:", gr_name)
221 1094acda Guido Trotter
    for item, val in gr_dict.iteritems():
222 1094acda Guido Trotter
      ToStdout("      %s: %s", item, val)
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
def ShowClusterConfig(opts, args):
225 a8083063 Iustin Pop
  """Shows cluster information.
226 a8083063 Iustin Pop
227 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
228 469ee405 Iustin Pop
  @type args: list
229 469ee405 Iustin Pop
  @param args: should be an empty list
230 469ee405 Iustin Pop
  @rtype: int
231 469ee405 Iustin Pop
  @return: the desired exit code
232 469ee405 Iustin Pop
233 a8083063 Iustin Pop
  """
234 2e7b8369 Iustin Pop
  cl = GetClient()
235 2e7b8369 Iustin Pop
  result = cl.QueryClusterInfo()
236 a8083063 Iustin Pop
237 3a24c527 Iustin Pop
  ToStdout("Cluster name: %s", result["name"])
238 a8083063 Iustin Pop
239 3a24c527 Iustin Pop
  ToStdout("Master node: %s", result["master"])
240 a8083063 Iustin Pop
241 3a24c527 Iustin Pop
  ToStdout("Architecture (this node): %s (%s)",
242 3a24c527 Iustin Pop
           result["architecture"][0], result["architecture"][1])
243 a8083063 Iustin Pop
244 02691904 Alexander Schreiber
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
245 3a24c527 Iustin Pop
  ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
246 469f88e1 Iustin Pop
247 3a24c527 Iustin Pop
  ToStdout("Hypervisor parameters:")
248 1094acda Guido Trotter
  _PrintGroupedParams(result["hvparams"])
249 469f88e1 Iustin Pop
250 3a24c527 Iustin Pop
  ToStdout("Cluster parameters:")
251 4b7735f9 Iustin Pop
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
252 a8001106 Guido Trotter
  ToStdout("  - master netdev: %s", result["master_netdev"])
253 a8001106 Guido Trotter
  ToStdout("  - default bridge: %s", result["default_bridge"])
254 a8001106 Guido Trotter
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
255 a8001106 Guido Trotter
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
256 4b7735f9 Iustin Pop
257 4b7735f9 Iustin Pop
  ToStdout("Default instance parameters:")
258 1094acda Guido Trotter
  _PrintGroupedParams(result["beparams"])
259 1094acda Guido Trotter
260 1094acda Guido Trotter
  ToStdout("Default nic parameters:")
261 1094acda Guido Trotter
  _PrintGroupedParams(result["nicparams"])
262 8a12ce45 Iustin Pop
263 a8083063 Iustin Pop
  return 0
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
266 a8083063 Iustin Pop
def ClusterCopyFile(opts, args):
267 a8083063 Iustin Pop
  """Copy a file from master to some nodes.
268 a8083063 Iustin Pop
269 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
270 469ee405 Iustin Pop
  @type args: list
271 469ee405 Iustin Pop
  @param args: should contain only one element, the path of
272 469ee405 Iustin Pop
      the file to be copied
273 469ee405 Iustin Pop
  @rtype: int
274 469ee405 Iustin Pop
  @return: the desired exit code
275 a8083063 Iustin Pop
276 a8083063 Iustin Pop
  """
277 b3989551 Iustin Pop
  filename = args[0]
278 b3989551 Iustin Pop
  if not os.path.exists(filename):
279 b3989551 Iustin Pop
    raise errors.OpPrereqError("No such filename '%s'" % filename)
280 b3989551 Iustin Pop
281 56bece1f Iustin Pop
  cl = GetClient()
282 56bece1f Iustin Pop
283 b3989551 Iustin Pop
  myname = utils.HostInfo().name
284 b3989551 Iustin Pop
285 56bece1f Iustin Pop
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
286 56bece1f Iustin Pop
287 4040a784 Iustin Pop
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
288 4040a784 Iustin Pop
  results = [name for name in results if name != myname]
289 e00ea635 Michael Hanselmann
290 56bece1f Iustin Pop
  srun = ssh.SshRunner(cluster_name=cluster_name)
291 b3989551 Iustin Pop
  for node in results:
292 b3989551 Iustin Pop
    if not srun.CopyFileToNode(node, filename):
293 3a24c527 Iustin Pop
      ToStderr("Copy of file %s to node %s failed", filename, node)
294 b3989551 Iustin Pop
295 a8083063 Iustin Pop
  return 0
296 a8083063 Iustin Pop
297 a8083063 Iustin Pop
298 a8083063 Iustin Pop
def RunClusterCommand(opts, args):
299 a8083063 Iustin Pop
  """Run a command on some nodes.
300 a8083063 Iustin Pop
301 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
302 469ee405 Iustin Pop
  @type args: list
303 469ee405 Iustin Pop
  @param args: should contain the command to be run and its arguments
304 469ee405 Iustin Pop
  @rtype: int
305 469ee405 Iustin Pop
  @return: the desired exit code
306 a8083063 Iustin Pop
307 a8083063 Iustin Pop
  """
308 56bece1f Iustin Pop
  cl = GetClient()
309 7688d0d3 Michael Hanselmann
310 a8083063 Iustin Pop
  command = " ".join(args)
311 4040a784 Iustin Pop
312 4040a784 Iustin Pop
  nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
313 56bece1f Iustin Pop
314 56bece1f Iustin Pop
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
315 56bece1f Iustin Pop
                                                    "master_node"])
316 b3989551 Iustin Pop
317 56bece1f Iustin Pop
  srun = ssh.SshRunner(cluster_name=cluster_name)
318 b3989551 Iustin Pop
319 7688d0d3 Michael Hanselmann
  # Make sure master node is at list end
320 b3989551 Iustin Pop
  if master_node in nodes:
321 b3989551 Iustin Pop
    nodes.remove(master_node)
322 b3989551 Iustin Pop
    nodes.append(master_node)
323 b3989551 Iustin Pop
324 b3989551 Iustin Pop
  for name in nodes:
325 b3989551 Iustin Pop
    result = srun.Run(name, "root", command)
326 3a24c527 Iustin Pop
    ToStdout("------------------------------------------------")
327 3a24c527 Iustin Pop
    ToStdout("node: %s", name)
328 3a24c527 Iustin Pop
    ToStdout("%s", result.output)
329 3a24c527 Iustin Pop
    ToStdout("return code = %s", result.exit_code)
330 b3989551 Iustin Pop
331 b3989551 Iustin Pop
  return 0
332 a8083063 Iustin Pop
333 a8083063 Iustin Pop
334 a8083063 Iustin Pop
def VerifyCluster(opts, args):
335 a8083063 Iustin Pop
  """Verify integrity of cluster, performing various test on nodes.
336 a8083063 Iustin Pop
337 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
338 469ee405 Iustin Pop
  @type args: list
339 469ee405 Iustin Pop
  @param args: should be an empty list
340 469ee405 Iustin Pop
  @rtype: int
341 469ee405 Iustin Pop
  @return: the desired exit code
342 a8083063 Iustin Pop
343 a8083063 Iustin Pop
  """
344 8d59409f Iustin Pop
  skip_checks = []
345 e54c4c5e Guido Trotter
  if opts.skip_nplusone_mem:
346 e54c4c5e Guido Trotter
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
347 e54c4c5e Guido Trotter
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
348 34290825 Michael Hanselmann
  if SubmitOpCode(op):
349 34290825 Michael Hanselmann
    return 0
350 34290825 Michael Hanselmann
  else:
351 34290825 Michael Hanselmann
    return 1
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
354 f4d4e184 Iustin Pop
def VerifyDisks(opts, args):
355 f4d4e184 Iustin Pop
  """Verify integrity of cluster disks.
356 f4d4e184 Iustin Pop
357 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
358 469ee405 Iustin Pop
  @type args: list
359 469ee405 Iustin Pop
  @param args: should be an empty list
360 469ee405 Iustin Pop
  @rtype: int
361 469ee405 Iustin Pop
  @return: the desired exit code
362 f4d4e184 Iustin Pop
363 f4d4e184 Iustin Pop
  """
364 f4d4e184 Iustin Pop
  op = opcodes.OpVerifyDisks()
365 f4d4e184 Iustin Pop
  result = SubmitOpCode(op)
366 dcde0241 Guido Trotter
  if not isinstance(result, (list, tuple)) or len(result) != 4:
367 f4d4e184 Iustin Pop
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
368 f4d4e184 Iustin Pop
369 b63ed789 Iustin Pop
  nodes, nlvm, instances, missing = result
370 b63ed789 Iustin Pop
371 f4d4e184 Iustin Pop
  if nodes:
372 3a24c527 Iustin Pop
    ToStdout("Nodes unreachable or with bad data:")
373 f4d4e184 Iustin Pop
    for name in nodes:
374 3a24c527 Iustin Pop
      ToStdout("\t%s", name)
375 f4d4e184 Iustin Pop
  retcode = constants.EXIT_SUCCESS
376 b63ed789 Iustin Pop
377 b63ed789 Iustin Pop
  if nlvm:
378 b63ed789 Iustin Pop
    for node, text in nlvm.iteritems():
379 3a24c527 Iustin Pop
      ToStdout("Error on node %s: LVM error: %s",
380 26f15862 Iustin Pop
               node, utils.SafeEncode(text[-400:]))
381 b63ed789 Iustin Pop
      retcode |= 1
382 3a24c527 Iustin Pop
      ToStdout("You need to fix these nodes first before fixing instances")
383 b63ed789 Iustin Pop
384 f4d4e184 Iustin Pop
  if instances:
385 f4d4e184 Iustin Pop
    for iname in instances:
386 b63ed789 Iustin Pop
      if iname in missing:
387 b63ed789 Iustin Pop
        continue
388 f4d4e184 Iustin Pop
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
389 f4d4e184 Iustin Pop
      try:
390 3a24c527 Iustin Pop
        ToStdout("Activating disks for instance '%s'", iname)
391 f4d4e184 Iustin Pop
        SubmitOpCode(op)
392 f4d4e184 Iustin Pop
      except errors.GenericError, err:
393 f4d4e184 Iustin Pop
        nret, msg = FormatError(err)
394 f4d4e184 Iustin Pop
        retcode |= nret
395 3a24c527 Iustin Pop
        ToStderr("Error activating disks for instance %s: %s", iname, msg)
396 b63ed789 Iustin Pop
397 b63ed789 Iustin Pop
  if missing:
398 b63ed789 Iustin Pop
    for iname, ival in missing.iteritems():
399 b63ed789 Iustin Pop
      all_missing = utils.all(ival, lambda x: x[0] in nlvm)
400 b63ed789 Iustin Pop
      if all_missing:
401 3a24c527 Iustin Pop
        ToStdout("Instance %s cannot be verified as it lives on"
402 3a24c527 Iustin Pop
                 " broken nodes", iname)
403 b63ed789 Iustin Pop
      else:
404 3a24c527 Iustin Pop
        ToStdout("Instance %s has missing logical volumes:", iname)
405 b63ed789 Iustin Pop
        ival.sort()
406 b63ed789 Iustin Pop
        for node, vol in ival:
407 b63ed789 Iustin Pop
          if node in nlvm:
408 3a24c527 Iustin Pop
            ToStdout("\tbroken node %s /dev/xenvg/%s", node, vol)
409 b63ed789 Iustin Pop
          else:
410 3a24c527 Iustin Pop
            ToStdout("\t%s /dev/xenvg/%s", node, vol)
411 3a24c527 Iustin Pop
    ToStdout("You need to run replace_disks for all the above"
412 b63ed789 Iustin Pop
           " instances, if this message persist after fixing nodes.")
413 b63ed789 Iustin Pop
    retcode |= 1
414 f4d4e184 Iustin Pop
415 f4d4e184 Iustin Pop
  return retcode
416 f4d4e184 Iustin Pop
417 f4d4e184 Iustin Pop
418 4331f6cd Michael Hanselmann
@UsesRPC
419 a8083063 Iustin Pop
def MasterFailover(opts, args):
420 a8083063 Iustin Pop
  """Failover the master node.
421 a8083063 Iustin Pop
422 a8083063 Iustin Pop
  This command, when run on a non-master node, will cause the current
423 a8083063 Iustin Pop
  master to cease being master, and the non-master to become new
424 a8083063 Iustin Pop
  master.
425 a8083063 Iustin Pop
426 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
427 469ee405 Iustin Pop
  @type args: list
428 469ee405 Iustin Pop
  @param args: should be an empty list
429 469ee405 Iustin Pop
  @rtype: int
430 469ee405 Iustin Pop
  @return: the desired exit code
431 469ee405 Iustin Pop
432 a8083063 Iustin Pop
  """
433 b1b6ea87 Iustin Pop
  return bootstrap.MasterFailover()
434 a8083063 Iustin Pop
435 a8083063 Iustin Pop
436 73415719 Iustin Pop
def SearchTags(opts, args):
437 73415719 Iustin Pop
  """Searches the tags on all the cluster.
438 73415719 Iustin Pop
439 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
440 469ee405 Iustin Pop
  @type args: list
441 469ee405 Iustin Pop
  @param args: should contain only one element, the tag pattern
442 469ee405 Iustin Pop
  @rtype: int
443 469ee405 Iustin Pop
  @return: the desired exit code
444 469ee405 Iustin Pop
445 73415719 Iustin Pop
  """
446 73415719 Iustin Pop
  op = opcodes.OpSearchTags(pattern=args[0])
447 73415719 Iustin Pop
  result = SubmitOpCode(op)
448 73415719 Iustin Pop
  if not result:
449 73415719 Iustin Pop
    return 1
450 73415719 Iustin Pop
  result = list(result)
451 73415719 Iustin Pop
  result.sort()
452 73415719 Iustin Pop
  for path, tag in result:
453 3a24c527 Iustin Pop
    ToStdout("%s %s", path, tag)
454 73415719 Iustin Pop
455 73415719 Iustin Pop
456 90b6aa3a Manuel Franceschini
def SetClusterParams(opts, args):
457 90b6aa3a Manuel Franceschini
  """Modify the cluster.
458 90b6aa3a Manuel Franceschini
459 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
460 469ee405 Iustin Pop
  @type args: list
461 469ee405 Iustin Pop
  @param args: should be an empty list
462 469ee405 Iustin Pop
  @rtype: int
463 469ee405 Iustin Pop
  @return: the desired exit code
464 90b6aa3a Manuel Franceschini
465 90b6aa3a Manuel Franceschini
  """
466 779c15bb Iustin Pop
  if not (not opts.lvm_storage or opts.vg_name or
467 779c15bb Iustin Pop
          opts.enabled_hypervisors or opts.hvparams or
468 5af3da74 Guido Trotter
          opts.beparams or opts.nicparams or
469 5af3da74 Guido Trotter
          opts.candidate_pool_size is not None):
470 3a24c527 Iustin Pop
    ToStderr("Please give at least one of the parameters.")
471 90b6aa3a Manuel Franceschini
    return 1
472 90b6aa3a Manuel Franceschini
473 90b6aa3a Manuel Franceschini
  vg_name = opts.vg_name
474 90b6aa3a Manuel Franceschini
  if not opts.lvm_storage and opts.vg_name:
475 3a24c527 Iustin Pop
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
476 90b6aa3a Manuel Franceschini
    return 1
477 b8a8fbe1 Guido Trotter
  elif not opts.lvm_storage:
478 b8a8fbe1 Guido Trotter
    vg_name = ''
479 90b6aa3a Manuel Franceschini
480 779c15bb Iustin Pop
  hvlist = opts.enabled_hypervisors
481 779c15bb Iustin Pop
  if hvlist is not None:
482 779c15bb Iustin Pop
    hvlist = hvlist.split(",")
483 779c15bb Iustin Pop
484 f8e7ddca Guido Trotter
  # a list of (name, dict) we can pass directly to dict() (or [])
485 f8e7ddca Guido Trotter
  hvparams = dict(opts.hvparams)
486 a5728081 Guido Trotter
  for hv, hv_params in hvparams.iteritems():
487 a5728081 Guido Trotter
    utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
488 779c15bb Iustin Pop
489 779c15bb Iustin Pop
  beparams = opts.beparams
490 a5728081 Guido Trotter
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
491 779c15bb Iustin Pop
492 5af3da74 Guido Trotter
  nicparams = opts.nicparams
493 5af3da74 Guido Trotter
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
494 5af3da74 Guido Trotter
495 b8a8fbe1 Guido Trotter
  op = opcodes.OpSetClusterParams(vg_name=vg_name,
496 779c15bb Iustin Pop
                                  enabled_hypervisors=hvlist,
497 779c15bb Iustin Pop
                                  hvparams=hvparams,
498 4b7735f9 Iustin Pop
                                  beparams=beparams,
499 5af3da74 Guido Trotter
                                  nicparams=nicparams,
500 4b7735f9 Iustin Pop
                                  candidate_pool_size=opts.candidate_pool_size)
501 90b6aa3a Manuel Franceschini
  SubmitOpCode(op)
502 90b6aa3a Manuel Franceschini
  return 0
503 90b6aa3a Manuel Franceschini
504 90b6aa3a Manuel Franceschini
505 3ccafd0e Iustin Pop
def QueueOps(opts, args):
506 3ccafd0e Iustin Pop
  """Queue operations.
507 3ccafd0e Iustin Pop
508 469ee405 Iustin Pop
  @param opts: the command line options selected by the user
509 469ee405 Iustin Pop
  @type args: list
510 469ee405 Iustin Pop
  @param args: should contain only one element, the subcommand
511 469ee405 Iustin Pop
  @rtype: int
512 469ee405 Iustin Pop
  @return: the desired exit code
513 469ee405 Iustin Pop
514 3ccafd0e Iustin Pop
  """
515 3ccafd0e Iustin Pop
  command = args[0]
516 3ccafd0e Iustin Pop
  client = GetClient()
517 3ccafd0e Iustin Pop
  if command in ("drain", "undrain"):
518 3ccafd0e Iustin Pop
    drain_flag = command == "drain"
519 3ccafd0e Iustin Pop
    client.SetQueueDrainFlag(drain_flag)
520 3ccafd0e Iustin Pop
  elif command == "info":
521 3ccafd0e Iustin Pop
    result = client.QueryConfigValues(["drain_flag"])
522 3ccafd0e Iustin Pop
    if result[0]:
523 3a24c527 Iustin Pop
      val = "set"
524 3ccafd0e Iustin Pop
    else:
525 3a24c527 Iustin Pop
      val = "unset"
526 3a24c527 Iustin Pop
    ToStdout("The drain flag is %s" % val)
527 2e668b38 Guido Trotter
  else:
528 2e668b38 Guido Trotter
    raise errors.OpPrereqError("Command '%s' is not valid." % command)
529 2e668b38 Guido Trotter
530 3ccafd0e Iustin Pop
  return 0
531 3ccafd0e Iustin Pop
532 a8083063 Iustin Pop
# this is an option common to more than one command, so we declare
533 a8083063 Iustin Pop
# it here and reuse it
534 a8083063 Iustin Pop
node_option = make_option("-n", "--node", action="append", dest="nodes",
535 f4bc1f2c Michael Hanselmann
                          help="Node to copy to (if not given, all nodes),"
536 f4bc1f2c Michael Hanselmann
                               " can be given multiple times",
537 f4bc1f2c Michael Hanselmann
                          metavar="<node>", default=[])
538 a8083063 Iustin Pop
539 a8083063 Iustin Pop
commands = {
540 a8083063 Iustin Pop
  'init': (InitCluster, ARGS_ONE,
541 a8083063 Iustin Pop
           [DEBUG_OPT,
542 a8083063 Iustin Pop
            make_option("-s", "--secondary-ip", dest="secondary_ip",
543 a8083063 Iustin Pop
                        help="Specify the secondary ip for this node;"
544 a8083063 Iustin Pop
                        " if given, the entire cluster must have secondary"
545 a8083063 Iustin Pop
                        " addresses",
546 a8083063 Iustin Pop
                        metavar="ADDRESS", default=None),
547 a8083063 Iustin Pop
            make_option("-m", "--mac-prefix", dest="mac_prefix",
548 a8083063 Iustin Pop
                        help="Specify the mac prefix for the instance IP"
549 a8083063 Iustin Pop
                        " addresses, in the format XX:XX:XX",
550 a8083063 Iustin Pop
                        metavar="PREFIX",
551 c5e489f7 Iustin Pop
                        default=constants.DEFAULT_MAC_PREFIX,),
552 a8083063 Iustin Pop
            make_option("-g", "--vg-name", dest="vg_name",
553 a8083063 Iustin Pop
                        help="Specify the volume group name "
554 a8083063 Iustin Pop
                        " (cluster-wide) for disk allocation [xenvg]",
555 a8083063 Iustin Pop
                        metavar="VG",
556 90b6aa3a Manuel Franceschini
                        default=None,),
557 a8083063 Iustin Pop
            make_option("-b", "--bridge", dest="def_bridge",
558 a8083063 Iustin Pop
                        help="Specify the default bridge name (cluster-wide)"
559 cf62a272 Michael Hanselmann
                          " to connect the instances to [%s]" %
560 cf62a272 Michael Hanselmann
                          constants.DEFAULT_BRIDGE,
561 a8083063 Iustin Pop
                        metavar="BRIDGE",
562 cf62a272 Michael Hanselmann
                        default=constants.DEFAULT_BRIDGE,),
563 880478f8 Iustin Pop
            make_option("--master-netdev", dest="master_netdev",
564 880478f8 Iustin Pop
                        help="Specify the node interface (cluster-wide)"
565 cf62a272 Michael Hanselmann
                          " on which the master IP address will be added "
566 cf62a272 Michael Hanselmann
                          " [%s]" % constants.DEFAULT_BRIDGE,
567 880478f8 Iustin Pop
                        metavar="NETDEV",
568 cf62a272 Michael Hanselmann
                        default=constants.DEFAULT_BRIDGE,),
569 79e82404 Manuel Franceschini
            make_option("--file-storage-dir", dest="file_storage_dir",
570 79e82404 Manuel Franceschini
                        help="Specify the default directory (cluster-wide)"
571 79e82404 Manuel Franceschini
                             " for storing the file-based disks [%s]" %
572 79e82404 Manuel Franceschini
                             constants.DEFAULT_FILE_STORAGE_DIR,
573 79e82404 Manuel Franceschini
                        metavar="DIR",
574 79e82404 Manuel Franceschini
                        default=constants.DEFAULT_FILE_STORAGE_DIR,),
575 90b6aa3a Manuel Franceschini
            make_option("--no-lvm-storage", dest="lvm_storage",
576 90b6aa3a Manuel Franceschini
                        help="No support for lvm based instances"
577 90b6aa3a Manuel Franceschini
                             " (cluster-wide)",
578 90b6aa3a Manuel Franceschini
                        action="store_false", default=True,),
579 b86a6bcd Guido Trotter
            make_option("--no-etc-hosts", dest="modify_etc_hosts",
580 b86a6bcd Guido Trotter
                        help="Don't modify /etc/hosts"
581 b86a6bcd Guido Trotter
                             " (cluster-wide)",
582 b86a6bcd Guido Trotter
                        action="store_false", default=True,),
583 ea3a925f Alexander Schreiber
            make_option("--enabled-hypervisors", dest="enabled_hypervisors",
584 ea3a925f Alexander Schreiber
                        help="Comma-separated list of hypervisors",
585 ea3a925f Alexander Schreiber
                        type="string", default=None),
586 02691904 Alexander Schreiber
            make_option("-t", "--default-hypervisor",
587 02691904 Alexander Schreiber
                        dest="default_hypervisor",
588 02691904 Alexander Schreiber
                        help="Default hypervisor to use for instance creation",
589 02691904 Alexander Schreiber
                        choices=list(constants.HYPER_TYPES),
590 02691904 Alexander Schreiber
                        default=constants.DEFAULT_ENABLED_HYPERVISOR),
591 ea3a925f Alexander Schreiber
            ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
592 ea3a925f Alexander Schreiber
                       help="Hypervisor and hypervisor options, in the"
593 ea3a925f Alexander Schreiber
                         " format"
594 ea3a925f Alexander Schreiber
                       " hypervisor:option=value,option=value,...",
595 ea3a925f Alexander Schreiber
                       default=[],
596 ea3a925f Alexander Schreiber
                       action="append",
597 ea3a925f Alexander Schreiber
                       type="identkeyval"),
598 ea3a925f Alexander Schreiber
            keyval_option("-B", "--backend-parameters", dest="beparams",
599 ea3a925f Alexander Schreiber
                          type="keyval", default={},
600 ea3a925f Alexander Schreiber
                          help="Backend parameters"),
601 b6a30b0d Guido Trotter
            keyval_option("-N", "--nic-parameters", dest="nicparams",
602 b6a30b0d Guido Trotter
                          type="keyval", default={},
603 b6a30b0d Guido Trotter
                          help="NIC parameters"),
604 ce735215 Guido Trotter
            make_option("-C", "--candidate-pool-size",
605 ce735215 Guido Trotter
                        default=constants.MASTER_POOL_SIZE_DEFAULT,
606 ce735215 Guido Trotter
                        help="Set the candidate pool size",
607 ce735215 Guido Trotter
                        dest="candidate_pool_size", type="int"),
608 a8083063 Iustin Pop
            ],
609 9a033156 Iustin Pop
           "[opts...] <cluster_name>",
610 a8083063 Iustin Pop
           "Initialises a new cluster configuration"),
611 a8083063 Iustin Pop
  'destroy': (DestroyCluster, ARGS_NONE,
612 a8083063 Iustin Pop
              [DEBUG_OPT,
613 a8083063 Iustin Pop
               make_option("--yes-do-it", dest="yes_do_it",
614 a8083063 Iustin Pop
                           help="Destroy cluster",
615 a8083063 Iustin Pop
                           action="store_true"),
616 a8083063 Iustin Pop
              ],
617 9a033156 Iustin Pop
              "", "Destroy cluster"),
618 07bd8a51 Iustin Pop
  'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
619 9a033156 Iustin Pop
               "<new_name>",
620 07bd8a51 Iustin Pop
               "Renames the cluster"),
621 afee0879 Iustin Pop
  'redist-conf': (RedistributeConfig, ARGS_NONE, [DEBUG_OPT, SUBMIT_OPT],
622 afee0879 Iustin Pop
                  "",
623 afee0879 Iustin Pop
                  "Forces a push of the configuration file and ssconf files"
624 afee0879 Iustin Pop
                  " to the nodes in the cluster"),
625 e54c4c5e Guido Trotter
  'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT,
626 e54c4c5e Guido Trotter
             make_option("--no-nplus1-mem", dest="skip_nplusone_mem",
627 e54c4c5e Guido Trotter
                         help="Skip N+1 memory redundancy tests",
628 e54c4c5e Guido Trotter
                         action="store_true",
629 e54c4c5e Guido Trotter
                         default=False,),
630 e54c4c5e Guido Trotter
             ],
631 9a033156 Iustin Pop
             "", "Does a check on the cluster configuration"),
632 f4d4e184 Iustin Pop
  'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
633 9a033156 Iustin Pop
                   "", "Does a check on the cluster disk status"),
634 a8083063 Iustin Pop
  'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
635 9a033156 Iustin Pop
                     "", "Makes the current node the master"),
636 a8083063 Iustin Pop
  'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
637 9a033156 Iustin Pop
              "", "Shows the cluster version"),
638 a8083063 Iustin Pop
  'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
639 9a033156 Iustin Pop
                "", "Shows the cluster master"),
640 a8083063 Iustin Pop
  'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
641 9a033156 Iustin Pop
               "[-n node...] <filename>",
642 a8083063 Iustin Pop
               "Copies a file to all (or only some) nodes"),
643 a8083063 Iustin Pop
  'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
644 9a033156 Iustin Pop
              "[-n node...] <command>",
645 a8083063 Iustin Pop
              "Runs a command on all (or only some) nodes"),
646 a8083063 Iustin Pop
  'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
647 9a033156 Iustin Pop
                 "", "Show cluster configuration"),
648 846baef9 Iustin Pop
  'list-tags': (ListTags, ARGS_NONE,
649 9a033156 Iustin Pop
                [DEBUG_OPT], "", "List the tags of the cluster"),
650 810c50b7 Iustin Pop
  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
651 9a033156 Iustin Pop
               "tag...", "Add tags to the cluster"),
652 810c50b7 Iustin Pop
  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
653 9a033156 Iustin Pop
                  "tag...", "Remove tags from the cluster"),
654 73415719 Iustin Pop
  'search-tags': (SearchTags, ARGS_ONE,
655 9a033156 Iustin Pop
                  [DEBUG_OPT], "", "Searches the tags on all objects on"
656 73415719 Iustin Pop
                  " the cluster for a given pattern (regex)"),
657 3ccafd0e Iustin Pop
  'queue': (QueueOps, ARGS_ONE, [DEBUG_OPT],
658 3ccafd0e Iustin Pop
            "drain|undrain|info", "Change queue properties"),
659 90b6aa3a Manuel Franceschini
  'modify': (SetClusterParams, ARGS_NONE,
660 90b6aa3a Manuel Franceschini
             [DEBUG_OPT,
661 90b6aa3a Manuel Franceschini
              make_option("-g", "--vg-name", dest="vg_name",
662 90b6aa3a Manuel Franceschini
                          help="Specify the volume group name "
663 90b6aa3a Manuel Franceschini
                          " (cluster-wide) for disk allocation "
664 90b6aa3a Manuel Franceschini
                          "and enable lvm based storage",
665 90b6aa3a Manuel Franceschini
                          metavar="VG",),
666 90b6aa3a Manuel Franceschini
              make_option("--no-lvm-storage", dest="lvm_storage",
667 90b6aa3a Manuel Franceschini
                          help="Disable support for lvm based instances"
668 90b6aa3a Manuel Franceschini
                               " (cluster-wide)",
669 90b6aa3a Manuel Franceschini
                          action="store_false", default=True,),
670 779c15bb Iustin Pop
              make_option("--enabled-hypervisors", dest="enabled_hypervisors",
671 779c15bb Iustin Pop
                          help="Comma-separated list of hypervisors",
672 779c15bb Iustin Pop
                          type="string", default=None),
673 779c15bb Iustin Pop
              ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
674 779c15bb Iustin Pop
                         help="Hypervisor and hypervisor options, in the"
675 779c15bb Iustin Pop
                         " format"
676 779c15bb Iustin Pop
                         " hypervisor:option=value,option=value,...",
677 779c15bb Iustin Pop
                         default=[],
678 779c15bb Iustin Pop
                         action="append",
679 779c15bb Iustin Pop
                         type="identkeyval"),
680 779c15bb Iustin Pop
              keyval_option("-B", "--backend-parameters", dest="beparams",
681 779c15bb Iustin Pop
                            type="keyval", default={},
682 779c15bb Iustin Pop
                            help="Backend parameters"),
683 5af3da74 Guido Trotter
              keyval_option("-N", "--nic-parameters", dest="nicparams",
684 5af3da74 Guido Trotter
                            type="keyval", default={},
685 5af3da74 Guido Trotter
                            help="NIC parameters"),
686 4b7735f9 Iustin Pop
              make_option("-C", "--candidate-pool-size", default=None,
687 4b7735f9 Iustin Pop
                          help="Set the candidate pool size",
688 4b7735f9 Iustin Pop
                          dest="candidate_pool_size", type="int"),
689 90b6aa3a Manuel Franceschini
              ],
690 90b6aa3a Manuel Franceschini
             "[opts...]",
691 90b6aa3a Manuel Franceschini
             "Alters the parameters of the cluster"),
692 a8083063 Iustin Pop
  }
693 a8083063 Iustin Pop
694 a8083063 Iustin Pop
if __name__ == '__main__':
695 846baef9 Iustin Pop
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}))