Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ b3989551

History | View | Annotate | Download (14.9 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
import os.path
26

    
27
from ganeti.cli import *
28
from ganeti import opcodes
29
from ganeti import constants
30
from ganeti import errors
31
from ganeti import utils
32
from ganeti import bootstrap
33
from ganeti import ssh
34
from ganeti import ssconf
35

    
36

    
37
def InitCluster(opts, args):
38
  """Initialize the cluster.
39

    
40
  Args:
41
    opts - class with options as members
42
    args - list of arguments, expected to be [clustername]
43

    
44
  """
45
  if not opts.lvm_storage and opts.vg_name:
46
    print ("Options --no-lvm-storage and --vg-name conflict.")
47
    return 1
48

    
49
  vg_name = opts.vg_name
50
  if opts.lvm_storage and not opts.vg_name:
51
    vg_name = constants.DEFAULT_VG
52

    
53
  bootstrap.InitCluster(cluster_name=args[0],
54
                        secondary_ip=opts.secondary_ip,
55
                        hypervisor_type=opts.hypervisor_type,
56
                        vg_name=vg_name,
57
                        mac_prefix=opts.mac_prefix,
58
                        def_bridge=opts.def_bridge,
59
                        master_netdev=opts.master_netdev,
60
                        file_storage_dir=opts.file_storage_dir)
61
  return 0
62

    
63

    
64
def DestroyCluster(opts, args):
65
  """Destroy the cluster.
66

    
67
  Args:
68
    opts - class with options as members
69

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

    
76
  op = opcodes.OpDestroyCluster()
77
  SubmitOpCode(op)
78
  return 0
79

    
80

    
81
def RenameCluster(opts, args):
82
  """Rename the cluster.
83

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

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

    
98
  op = opcodes.OpRenameCluster(name=name)
99
  SubmitOpCode(op)
100
  return 0
101

    
102

    
103
def ShowClusterVersion(opts, args):
104
  """Write version of ganeti software to the standard output.
105

    
106
  Args:
107
    opts - class with options as members
108

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

    
119

    
120
def ShowClusterMaster(opts, args):
121
  """Write name of master node to the standard output.
122

    
123
  Args:
124
    opts - class with options as members
125

    
126
  """
127
  op = opcodes.OpQueryClusterInfo()
128
  result = SubmitOpCode(op)
129
  print (result["master"])
130
  return 0
131

    
132

    
133
def ShowClusterConfig(opts, args):
134
  """Shows cluster information.
135

    
136
  """
137
  op = opcodes.OpQueryClusterInfo()
138
  result = SubmitOpCode(op)
139

    
140
  print ("Cluster name: %s" % result["name"])
141

    
142
  print ("Master node: %s" % result["master"])
143

    
144
  print ("Architecture (this node): %s (%s)" %
145
         (result["architecture"][0], result["architecture"][1]))
146

    
147
  print ("Cluster hypervisor: %s" % result["hypervisor_type"])
148

    
149
  return 0
150

    
151

    
152
def ClusterCopyFile(opts, args):
153
  """Copy a file from master to some nodes.
154

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

    
161
  """
162
  filename = args[0]
163
  if not os.path.exists(filename):
164
    raise errors.OpPrereqError("No such filename '%s'" % filename)
165

    
166
  myname = utils.HostInfo().name
167

    
168
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
169
  results = [row[0] for row in SubmitOpCode(op) if row[0] != myname]
170
  srun = ssh.SshRunner()
171
  for node in results:
172
    if not srun.CopyFileToNode(node, filename):
173
      print >> sys.stderr, ("Copy of file %s to node %s failed" %
174
                            (filename, node))
175

    
176
  return 0
177

    
178

    
179
def RunClusterCommand(opts, args):
180
  """Run a command on some nodes.
181

    
182
  Args:
183
    opts - class with options as members
184
    args - the command list as a list
185
  Opts used:
186
    nodes: list containing the name of target nodes; if empty, all nodes
187

    
188
  """
189
  command = " ".join(args)
190
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
191
  nodes = [row[0] for row in SubmitOpCode(op)]
192

    
193
  sstore = ssconf.SimpleStore()
194
  master_node = sstore.GetMasterNode()
195
  srun = ssh.SshRunner(sstore=sstore)
196

    
197
  if master_node in nodes:
198
    nodes.remove(master_node)
199
    nodes.append(master_node)
200

    
201
  for name in nodes:
202
    result = srun.Run(name, "root", command)
203
    print ("------------------------------------------------")
204
    print ("node: %s" % name)
205
    print ("%s" % result.output)
206
    print ("return code = %s" % result.exit_code)
207

    
208
  return 0
209

    
210

    
211
def VerifyCluster(opts, args):
212
  """Verify integrity of cluster, performing various test on nodes.
213

    
214
  Args:
215
    opts - class with options as members
216

    
217
  """
218
  skip_checks = []
219
  if opts.skip_nplusone_mem:
220
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
221
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
222
  result = SubmitOpCode(op)
223
  return result
224

    
225

    
226
def VerifyDisks(opts, args):
227
  """Verify integrity of cluster disks.
228

    
229
  Args:
230
    opts - class with options as members
231

    
232
  """
233
  op = opcodes.OpVerifyDisks()
234
  result = SubmitOpCode(op)
235
  if not isinstance(result, tuple) or len(result) != 4:
236
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
237

    
238
  nodes, nlvm, instances, missing = result
239

    
240
  if nodes:
241
    print "Nodes unreachable or with bad data:"
242
    for name in nodes:
243
      print "\t%s" % name
244
  retcode = constants.EXIT_SUCCESS
245

    
246
  if nlvm:
247
    for node, text in nlvm.iteritems():
248
      print ("Error on node %s: LVM error: %s" %
249
             (node, text[-400:].encode('string_escape')))
250
      retcode |= 1
251
      print "You need to fix these nodes first before fixing instances"
252

    
253
  if instances:
254
    for iname in instances:
255
      if iname in missing:
256
        continue
257
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
258
      try:
259
        print "Activating disks for instance '%s'" % iname
260
        SubmitOpCode(op)
261
      except errors.GenericError, err:
262
        nret, msg = FormatError(err)
263
        retcode |= nret
264
        print >> sys.stderr, ("Error activating disks for instance %s: %s" %
265
                              (iname, msg))
266

    
267
  if missing:
268
    for iname, ival in missing.iteritems():
269
      all_missing = utils.all(ival, lambda x: x[0] in nlvm)
270
      if all_missing:
271
        print ("Instance %s cannot be verified as it lives on"
272
               " broken nodes" % iname)
273
      else:
274
        print "Instance %s has missing logical volumes:" % iname
275
        ival.sort()
276
        for node, vol in ival:
277
          if node in nlvm:
278
            print ("\tbroken node %s /dev/xenvg/%s" % (node, vol))
279
          else:
280
            print ("\t%s /dev/xenvg/%s" % (node, vol))
281
    print ("You need to run replace_disks for all the above"
282
           " instances, if this message persist after fixing nodes.")
283
    retcode |= 1
284

    
285
  return retcode
286

    
287

    
288
def MasterFailover(opts, args):
289
  """Failover the master node.
290

    
291
  This command, when run on a non-master node, will cause the current
292
  master to cease being master, and the non-master to become new
293
  master.
294

    
295
  """
296
  op = opcodes.OpMasterFailover()
297
  SubmitOpCode(op)
298

    
299

    
300
def SearchTags(opts, args):
301
  """Searches the tags on all the cluster.
302

    
303
  """
304
  op = opcodes.OpSearchTags(pattern=args[0])
305
  result = SubmitOpCode(op)
306
  if not result:
307
    return 1
308
  result = list(result)
309
  result.sort()
310
  for path, tag in result:
311
    print "%s %s" % (path, tag)
312

    
313

    
314
def SetClusterParams(opts, args):
315
  """Modify the cluster.
316

    
317
  Args:
318
    opts - class with options as members
319

    
320
  """
321
  if not (not opts.lvm_storage or opts.vg_name):
322
    print "Please give at least one of the parameters."
323
    return 1
324

    
325
  vg_name = opts.vg_name
326
  if not opts.lvm_storage and opts.vg_name:
327
    print ("Options --no-lvm-storage and --vg-name conflict.")
328
    return 1
329

    
330
  op = opcodes.OpSetClusterParams(vg_name=opts.vg_name)
331
  SubmitOpCode(op)
332
  return 0
333

    
334

    
335
# this is an option common to more than one command, so we declare
336
# it here and reuse it
337
node_option = make_option("-n", "--node", action="append", dest="nodes",
338
                          help="Node to copy to (if not given, all nodes),"
339
                               " can be given multiple times",
340
                          metavar="<node>", default=[])
341

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

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