Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 8a12ce45

History | View | Annotate | Download (14.2 kB)

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
from ganeti import errors
30
from ganeti import utils
31

    
32

    
33
def InitCluster(opts, args):
34
  """Initialize the cluster.
35

    
36
  Args:
37
    opts - class with options as members
38
    args - list of arguments, expected to be [clustername]
39

    
40
  """
41
  if not opts.lvm_storage and opts.vg_name:
42
    print ("Options --no-lvm-storage and --vg-name conflict.")
43
    return 1
44

    
45
  vg_name = opts.vg_name
46
  if opts.lvm_storage and not opts.vg_name:
47
    vg_name = constants.DEFAULT_VG
48

    
49
  op = opcodes.OpInitCluster(cluster_name=args[0],
50
                             secondary_ip=opts.secondary_ip,
51
                             hypervisor_type=opts.hypervisor_type,
52
                             vg_name=vg_name,
53
                             mac_prefix=opts.mac_prefix,
54
                             def_bridge=opts.def_bridge,
55
                             master_netdev=opts.master_netdev,
56
                             file_storage_dir=opts.file_storage_dir)
57
  SubmitOpCode(op)
58
  return 0
59

    
60

    
61
def DestroyCluster(opts, args):
62
  """Destroy the cluster.
63

    
64
  Args:
65
    opts - class with options as members
66

    
67
  """
68
  if not opts.yes_do_it:
69
    print ("Destroying a cluster is irreversibly. If you really want destroy"
70
           " this cluster, supply the --yes-do-it option.")
71
    return 1
72

    
73
  op = opcodes.OpDestroyCluster()
74
  SubmitOpCode(op)
75
  return 0
76

    
77

    
78
def RenameCluster(opts, args):
79
  """Rename the cluster.
80

    
81
  Args:
82
    opts - class with options as members, we use force only
83
    args - list of arguments, expected to be [new_name]
84

    
85
  """
86
  name = args[0]
87
  if not opts.force:
88
    usertext = ("This will rename the cluster to '%s'. If you are connected"
89
                " over the network to the cluster name, the operation is very"
90
                " dangerous as the IP address will be removed from the node"
91
                " and the change may not go through. Continue?") % name
92
    if not AskUser(usertext):
93
      return 1
94

    
95
  op = opcodes.OpRenameCluster(name=name)
96
  SubmitOpCode(op)
97
  return 0
98

    
99

    
100
def ShowClusterVersion(opts, args):
101
  """Write version of ganeti software to the standard output.
102

    
103
  Args:
104
    opts - class with options as members
105

    
106
  """
107
  op = opcodes.OpQueryClusterInfo()
108
  result = SubmitOpCode(op)
109
  print ("Software version: %s" % result["software_version"])
110
  print ("Internode protocol: %s" % result["protocol_version"])
111
  print ("Configuration format: %s" % result["config_version"])
112
  print ("OS api version: %s" % result["os_api_version"])
113
  print ("Export interface: %s" % result["export_version"])
114
  return 0
115

    
116

    
117
def ShowClusterMaster(opts, args):
118
  """Write name of master node to the standard output.
119

    
120
  Args:
121
    opts - class with options as members
122

    
123
  """
124
  op = opcodes.OpQueryClusterInfo()
125
  result = SubmitOpCode(op)
126
  print (result["master"])
127
  return 0
128

    
129

    
130
def ShowClusterConfig(opts, args):
131
  """Shows cluster information.
132

    
133
  """
134
  op = opcodes.OpQueryClusterInfo()
135
  result = SubmitOpCode(op)
136

    
137
  print ("Cluster name: %s" % result["name"])
138

    
139
  print ("Master node: %s" % result["master"])
140

    
141
  print ("Architecture (this node): %s (%s)" %
142
         (result["architecture"][0], result["architecture"][1]))
143

    
144
  print ("Cluster hypervisor: %s" % result["hypervisor_type"])
145

    
146
  return 0
147

    
148

    
149
def ClusterCopyFile(opts, args):
150
  """Copy a file from master to some nodes.
151

    
152
  Args:
153
    opts - class with options as members
154
    args - list containing a single element, the file name
155
  Opts used:
156
    nodes - list containing the name of target nodes; if empty, all nodes
157

    
158
  """
159
  op = opcodes.OpClusterCopyFile(filename=args[0], nodes=opts.nodes)
160
  SubmitOpCode(op)
161
  return 0
162

    
163

    
164
def RunClusterCommand(opts, args):
165
  """Run a command on some nodes.
166

    
167
  Args:
168
    opts - class with options as members
169
    args - the command list as a list
170
  Opts used:
171
    nodes: list containing the name of target nodes; if empty, all nodes
172

    
173
  """
174
  command = " ".join(args)
175
  nodes = opts.nodes
176
  op = opcodes.OpRunClusterCommand(command=command, nodes=nodes)
177
  result = SubmitOpCode(op)
178
  for node, output, exit_code in result:
179
    print ("------------------------------------------------")
180
    print ("node: %s" % node)
181
    print ("%s" % output)
182
    print ("return code = %s" % exit_code)
183

    
184

    
185
def VerifyCluster(opts, args):
186
  """Verify integrity of cluster, performing various test on nodes.
187

    
188
  Args:
189
    opts - class with options as members
190

    
191
  """
192
  skip_checks = []
193
  if opts.skip_nplusone_mem:
194
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
195
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
196
  result = SubmitOpCode(op)
197
  return result
198

    
199

    
200
def VerifyDisks(opts, args):
201
  """Verify integrity of cluster disks.
202

    
203
  Args:
204
    opts - class with options as members
205

    
206
  """
207
  op = opcodes.OpVerifyDisks()
208
  result = SubmitOpCode(op)
209
  if not isinstance(result, tuple) or len(result) != 4:
210
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
211

    
212
  nodes, nlvm, instances, missing = result
213

    
214
  if nodes:
215
    print "Nodes unreachable or with bad data:"
216
    for name in nodes:
217
      print "\t%s" % name
218
  retcode = constants.EXIT_SUCCESS
219

    
220
  if nlvm:
221
    for node, text in nlvm.iteritems():
222
      print ("Error on node %s: LVM error: %s" %
223
             (node, text[-400:].encode('string_escape')))
224
      retcode |= 1
225
      print "You need to fix these nodes first before fixing instances"
226

    
227
  if instances:
228
    for iname in instances:
229
      if iname in missing:
230
        continue
231
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
232
      try:
233
        print "Activating disks for instance '%s'" % iname
234
        SubmitOpCode(op)
235
      except errors.GenericError, err:
236
        nret, msg = FormatError(err)
237
        retcode |= nret
238
        print >> sys.stderr, ("Error activating disks for instance %s: %s" %
239
                              (iname, msg))
240

    
241
  if missing:
242
    for iname, ival in missing.iteritems():
243
      all_missing = utils.all(ival, lambda x: x[0] in nlvm)
244
      if all_missing:
245
        print ("Instance %s cannot be verified as it lives on"
246
               " broken nodes" % iname)
247
      else:
248
        print "Instance %s has missing logical volumes:" % iname
249
        ival.sort()
250
        for node, vol in ival:
251
          if node in nlvm:
252
            print ("\tbroken node %s /dev/xenvg/%s" % (node, vol))
253
          else:
254
            print ("\t%s /dev/xenvg/%s" % (node, vol))
255
    print ("You need to run replace_disks for all the above"
256
           " instances, if this message persist after fixing nodes.")
257
    retcode |= 1
258

    
259
  return retcode
260

    
261

    
262
def MasterFailover(opts, args):
263
  """Failover the master node.
264

    
265
  This command, when run on a non-master node, will cause the current
266
  master to cease being master, and the non-master to become new
267
  master.
268

    
269
  """
270
  op = opcodes.OpMasterFailover()
271
  SubmitOpCode(op)
272

    
273

    
274
def SearchTags(opts, args):
275
  """Searches the tags on all the cluster.
276

    
277
  """
278
  op = opcodes.OpSearchTags(pattern=args[0])
279
  result = SubmitOpCode(op)
280
  if not result:
281
    return 1
282
  result = list(result)
283
  result.sort()
284
  for path, tag in result:
285
    print "%s %s" % (path, tag)
286

    
287

    
288
def SetClusterParams(opts, args):
289
  """Modify the cluster.
290

    
291
  Args:
292
    opts - class with options as members
293

    
294
  """
295
  if not (not opts.lvm_storage or opts.vg_name):
296
    print "Please give at least one of the parameters."
297
    return 1
298

    
299
  vg_name = opts.vg_name
300
  if not opts.lvm_storage and opts.vg_name:
301
    print ("Options --no-lvm-storage and --vg-name conflict.")
302
    return 1
303

    
304
  op = opcodes.OpSetClusterParams(vg_name=opts.vg_name)
305
  SubmitOpCode(op)
306
  return 0
307

    
308

    
309
# this is an option common to more than one command, so we declare
310
# it here and reuse it
311
node_option = make_option("-n", "--node", action="append", dest="nodes",
312
                          help="Node to copy to (if not given, all nodes),"
313
                               " can be given multiple times",
314
                          metavar="<node>", default=[])
315

    
316
commands = {
317
  'init': (InitCluster, ARGS_ONE,
318
           [DEBUG_OPT,
319
            make_option("-s", "--secondary-ip", dest="secondary_ip",
320
                        help="Specify the secondary ip for this node;"
321
                        " if given, the entire cluster must have secondary"
322
                        " addresses",
323
                        metavar="ADDRESS", default=None),
324
            make_option("-t", "--hypervisor-type", dest="hypervisor_type",
325
                        help="Specify the hypervisor type "
326
                        "(xen-3.0, fake, xen-hvm-3.1)",
327
                        metavar="TYPE", choices=["xen-3.0",
328
                                                 "fake",
329
                                                 "xen-hvm-3.1"],
330
                        default="xen-3.0",),
331
            make_option("-m", "--mac-prefix", dest="mac_prefix",
332
                        help="Specify the mac prefix for the instance IP"
333
                        " addresses, in the format XX:XX:XX",
334
                        metavar="PREFIX",
335
                        default="aa:00:00",),
336
            make_option("-g", "--vg-name", dest="vg_name",
337
                        help="Specify the volume group name "
338
                        " (cluster-wide) for disk allocation [xenvg]",
339
                        metavar="VG",
340
                        default=None,),
341
            make_option("-b", "--bridge", dest="def_bridge",
342
                        help="Specify the default bridge name (cluster-wide)"
343
                          " to connect the instances to [%s]" %
344
                          constants.DEFAULT_BRIDGE,
345
                        metavar="BRIDGE",
346
                        default=constants.DEFAULT_BRIDGE,),
347
            make_option("--master-netdev", dest="master_netdev",
348
                        help="Specify the node interface (cluster-wide)"
349
                          " on which the master IP address will be added "
350
                          " [%s]" % constants.DEFAULT_BRIDGE,
351
                        metavar="NETDEV",
352
                        default=constants.DEFAULT_BRIDGE,),
353
            make_option("--file-storage-dir", dest="file_storage_dir",
354
                        help="Specify the default directory (cluster-wide)"
355
                             " for storing the file-based disks [%s]" %
356
                             constants.DEFAULT_FILE_STORAGE_DIR,
357
                        metavar="DIR",
358
                        default=constants.DEFAULT_FILE_STORAGE_DIR,),
359
            make_option("--no-lvm-storage", dest="lvm_storage",
360
                        help="No support for lvm based instances"
361
                             " (cluster-wide)",
362
                        action="store_false", default=True,),
363
            ],
364
           "[opts...] <cluster_name>",
365
           "Initialises a new cluster configuration"),
366
  'destroy': (DestroyCluster, ARGS_NONE,
367
              [DEBUG_OPT,
368
               make_option("--yes-do-it", dest="yes_do_it",
369
                           help="Destroy cluster",
370
                           action="store_true"),
371
              ],
372
              "", "Destroy cluster"),
373
  'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
374
               "<new_name>",
375
               "Renames the cluster"),
376
  'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT,
377
             make_option("--no-nplus1-mem", dest="skip_nplusone_mem",
378
                         help="Skip N+1 memory redundancy tests",
379
                         action="store_true",
380
                         default=False,),
381
             ],
382
             "", "Does a check on the cluster configuration"),
383
  'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
384
                   "", "Does a check on the cluster disk status"),
385
  'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
386
                     "", "Makes the current node the master"),
387
  'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
388
              "", "Shows the cluster version"),
389
  'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
390
                "", "Shows the cluster master"),
391
  'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
392
               "[-n node...] <filename>",
393
               "Copies a file to all (or only some) nodes"),
394
  'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
395
              "[-n node...] <command>",
396
              "Runs a command on all (or only some) nodes"),
397
  'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
398
                 "", "Show cluster configuration"),
399
  'list-tags': (ListTags, ARGS_NONE,
400
                [DEBUG_OPT], "", "List the tags of the cluster"),
401
  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
402
               "tag...", "Add tags to the cluster"),
403
  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
404
                  "tag...", "Remove tags from the cluster"),
405
  'search-tags': (SearchTags, ARGS_ONE,
406
                  [DEBUG_OPT], "", "Searches the tags on all objects on"
407
                  " the cluster for a given pattern (regex)"),
408
  'modify': (SetClusterParams, ARGS_NONE,
409
             [DEBUG_OPT,
410
              make_option("-g", "--vg-name", dest="vg_name",
411
                          help="Specify the volume group name "
412
                          " (cluster-wide) for disk allocation "
413
                          "and enable lvm based storage",
414
                          metavar="VG",),
415
              make_option("--no-lvm-storage", dest="lvm_storage",
416
                          help="Disable support for lvm based instances"
417
                               " (cluster-wide)",
418
                          action="store_false", default=True,),
419
              ],
420
             "[opts...]",
421
             "Alters the parameters of the cluster"),
422
  }
423

    
424
if __name__ == '__main__':
425
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}))