Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 79e82404

History | View | Annotate | Download (12.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
  op = opcodes.OpInitCluster(cluster_name=args[0],
42
                             secondary_ip=opts.secondary_ip,
43
                             hypervisor_type=opts.hypervisor_type,
44
                             vg_name=opts.vg_name,
45
                             mac_prefix=opts.mac_prefix,
46
                             def_bridge=opts.def_bridge,
47
                             master_netdev=opts.master_netdev,
48
                             file_storage_dir=opts.file_storage_dir)
49
  SubmitOpCode(op)
50
  return 0
51

    
52

    
53
def DestroyCluster(opts, args):
54
  """Destroy the cluster.
55

    
56
  Args:
57
    opts - class with options as members
58

    
59
  """
60
  if not opts.yes_do_it:
61
    print ("Destroying a cluster is irreversibly. If you really want destroy"
62
           " this cluster, supply the --yes-do-it option.")
63
    return 1
64

    
65
  op = opcodes.OpDestroyCluster()
66
  SubmitOpCode(op)
67
  return 0
68

    
69

    
70
def RenameCluster(opts, args):
71
  """Rename the cluster.
72

    
73
  Args:
74
    opts - class with options as members, we use force only
75
    args - list of arguments, expected to be [new_name]
76

    
77
  """
78
  name = args[0]
79
  if not opts.force:
80
    usertext = ("This will rename the cluster to '%s'. If you are connected"
81
                " over the network to the cluster name, the operation is very"
82
                " dangerous as the IP address will be removed from the node"
83
                " and the change may not go through. Continue?") % name
84
    if not AskUser(usertext):
85
      return 1
86

    
87
  op = opcodes.OpRenameCluster(name=name)
88
  SubmitOpCode(op)
89
  return 0
90

    
91

    
92
def ShowClusterVersion(opts, args):
93
  """Write version of ganeti software to the standard output.
94

    
95
  Args:
96
    opts - class with options as members
97

    
98
  """
99
  op = opcodes.OpQueryClusterInfo()
100
  result = SubmitOpCode(op)
101
  print ("Software version: %s" % result["software_version"])
102
  print ("Internode protocol: %s" % result["protocol_version"])
103
  print ("Configuration format: %s" % result["config_version"])
104
  print ("OS api version: %s" % result["os_api_version"])
105
  print ("Export interface: %s" % result["export_version"])
106
  return 0
107

    
108

    
109
def ShowClusterMaster(opts, args):
110
  """Write name of master node to the standard output.
111

    
112
  Args:
113
    opts - class with options as members
114

    
115
  """
116
  op = opcodes.OpQueryClusterInfo()
117
  result = SubmitOpCode(op)
118
  print (result["master"])
119
  return 0
120

    
121

    
122
def ShowClusterConfig(opts, args):
123
  """Shows cluster information.
124

    
125
  """
126
  op = opcodes.OpQueryClusterInfo()
127
  result = SubmitOpCode(op)
128

    
129
  print ("Cluster name: %s" % result["name"])
130

    
131
  print ("Master node: %s" % result["master"])
132

    
133
  print ("Architecture (this node): %s (%s)" %
134
         (result["architecture"][0], result["architecture"][1]))
135

    
136
  return 0
137

    
138

    
139
def ClusterCopyFile(opts, args):
140
  """Copy a file from master to some nodes.
141

    
142
  Args:
143
    opts - class with options as members
144
    args - list containing a single element, the file name
145
  Opts used:
146
    nodes - list containing the name of target nodes; if empty, all nodes
147

    
148
  """
149
  op = opcodes.OpClusterCopyFile(filename=args[0], nodes=opts.nodes)
150
  SubmitOpCode(op)
151
  return 0
152

    
153

    
154
def RunClusterCommand(opts, args):
155
  """Run a command on some nodes.
156

    
157
  Args:
158
    opts - class with options as members
159
    args - the command list as a list
160
  Opts used:
161
    nodes: list containing the name of target nodes; if empty, all nodes
162

    
163
  """
164
  command = " ".join(args)
165
  nodes = opts.nodes
166
  op = opcodes.OpRunClusterCommand(command=command, nodes=nodes)
167
  result = SubmitOpCode(op)
168
  for node, output, exit_code in result:
169
    print ("------------------------------------------------")
170
    print ("node: %s" % node)
171
    print ("%s" % output)
172
    print ("return code = %s" % exit_code)
173

    
174

    
175
def VerifyCluster(opts, args):
176
  """Verify integrity of cluster, performing various test on nodes.
177

    
178
  Args:
179
    opts - class with options as members
180

    
181
  """
182
  op = opcodes.OpVerifyCluster()
183
  result = SubmitOpCode(op)
184
  return result
185

    
186

    
187
def VerifyDisks(opts, args):
188
  """Verify integrity of cluster disks.
189

    
190
  Args:
191
    opts - class with options as members
192

    
193
  """
194
  op = opcodes.OpVerifyDisks()
195
  result = SubmitOpCode(op)
196
  if not isinstance(result, tuple) or len(result) != 4:
197
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
198

    
199
  nodes, nlvm, instances, missing = result
200

    
201
  if nodes:
202
    print "Nodes unreachable or with bad data:"
203
    for name in nodes:
204
      print "\t%s" % name
205
  retcode = constants.EXIT_SUCCESS
206

    
207
  if nlvm:
208
    for node, text in nlvm.iteritems():
209
      print ("Error on node %s: LVM error: %s" %
210
             (node, text[-400:].encode('string_escape')))
211
      retcode |= 1
212
      print "You need to fix these nodes first before fixing instances"
213

    
214
  if instances:
215
    for iname in instances:
216
      if iname in missing:
217
        continue
218
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
219
      try:
220
        print "Activating disks for instance '%s'" % iname
221
        SubmitOpCode(op)
222
      except errors.GenericError, err:
223
        nret, msg = FormatError(err)
224
        retcode |= nret
225
        print >> sys.stderr, ("Error activating disks for instance %s: %s" %
226
                              (iname, msg))
227

    
228
  if missing:
229
    for iname, ival in missing.iteritems():
230
      all_missing = utils.all(ival, lambda x: x[0] in nlvm)
231
      if all_missing:
232
        print ("Instance %s cannot be verified as it lives on"
233
               " broken nodes" % iname)
234
      else:
235
        print "Instance %s has missing logical volumes:" % iname
236
        ival.sort()
237
        for node, vol in ival:
238
          if node in nlvm:
239
            print ("\tbroken node %s /dev/xenvg/%s" % (node, vol))
240
          else:
241
            print ("\t%s /dev/xenvg/%s" % (node, vol))
242
    print ("You need to run replace_disks for all the above"
243
           " instances, if this message persist after fixing nodes.")
244
    retcode |= 1
245

    
246
  return retcode
247

    
248

    
249
def MasterFailover(opts, args):
250
  """Failover the master node.
251

    
252
  This command, when run on a non-master node, will cause the current
253
  master to cease being master, and the non-master to become new
254
  master.
255

    
256
  """
257
  op = opcodes.OpMasterFailover()
258
  SubmitOpCode(op)
259

    
260

    
261
def SearchTags(opts, args):
262
  """Searches the tags on all the cluster.
263

    
264
  """
265
  op = opcodes.OpSearchTags(pattern=args[0])
266
  result = SubmitOpCode(op)
267
  if not result:
268
    return 1
269
  result = list(result)
270
  result.sort()
271
  for path, tag in result:
272
    print "%s %s" % (path, tag)
273

    
274

    
275
# this is an option common to more than one command, so we declare
276
# it here and reuse it
277
node_option = make_option("-n", "--node", action="append", dest="nodes",
278
                          help="Node to copy to (if not given, all nodes),"
279
                               " can be given multiple times",
280
                          metavar="<node>", default=[])
281

    
282
commands = {
283
  'init': (InitCluster, ARGS_ONE,
284
           [DEBUG_OPT,
285
            make_option("-s", "--secondary-ip", dest="secondary_ip",
286
                        help="Specify the secondary ip for this node;"
287
                        " if given, the entire cluster must have secondary"
288
                        " addresses",
289
                        metavar="ADDRESS", default=None),
290
            make_option("-t", "--hypervisor-type", dest="hypervisor_type",
291
                        help="Specify the hypervisor type "
292
                        "(xen-3.0, fake, xen-hvm-3.1)",
293
                        metavar="TYPE", choices=["xen-3.0",
294
                                                 "fake",
295
                                                 "xen-hvm-3.1"],
296
                        default="xen-3.0",),
297
            make_option("-m", "--mac-prefix", dest="mac_prefix",
298
                        help="Specify the mac prefix for the instance IP"
299
                        " addresses, in the format XX:XX:XX",
300
                        metavar="PREFIX",
301
                        default="aa:00:00",),
302
            make_option("-g", "--vg-name", dest="vg_name",
303
                        help="Specify the volume group name "
304
                        " (cluster-wide) for disk allocation [xenvg]",
305
                        metavar="VG",
306
                        default="xenvg",),
307
            make_option("-b", "--bridge", dest="def_bridge",
308
                        help="Specify the default bridge name (cluster-wide)"
309
                          " to connect the instances to [%s]" %
310
                          constants.DEFAULT_BRIDGE,
311
                        metavar="BRIDGE",
312
                        default=constants.DEFAULT_BRIDGE,),
313
            make_option("--master-netdev", dest="master_netdev",
314
                        help="Specify the node interface (cluster-wide)"
315
                          " on which the master IP address will be added "
316
                          " [%s]" % constants.DEFAULT_BRIDGE,
317
                        metavar="NETDEV",
318
                        default=constants.DEFAULT_BRIDGE,),
319
            make_option("--file-storage-dir", dest="file_storage_dir",
320
                        help="Specify the default directory (cluster-wide)"
321
                             " for storing the file-based disks [%s]" %
322
                             constants.DEFAULT_FILE_STORAGE_DIR,
323
                        metavar="DIR",
324
                        default=constants.DEFAULT_FILE_STORAGE_DIR,),
325
            ],
326
           "[opts...] <cluster_name>",
327
           "Initialises a new cluster configuration"),
328
  'destroy': (DestroyCluster, ARGS_NONE,
329
              [DEBUG_OPT,
330
               make_option("--yes-do-it", dest="yes_do_it",
331
                           help="Destroy cluster",
332
                           action="store_true"),
333
              ],
334
              "", "Destroy cluster"),
335
  'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
336
               "<new_name>",
337
               "Renames the cluster"),
338
  'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT],
339
             "", "Does a check on the cluster configuration"),
340
  'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
341
                   "", "Does a check on the cluster disk status"),
342
  'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
343
                     "", "Makes the current node the master"),
344
  'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
345
              "", "Shows the cluster version"),
346
  'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
347
                "", "Shows the cluster master"),
348
  'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
349
               "[-n node...] <filename>",
350
               "Copies a file to all (or only some) nodes"),
351
  'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
352
              "[-n node...] <command>",
353
              "Runs a command on all (or only some) nodes"),
354
  'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
355
                 "", "Show cluster configuration"),
356
  'list-tags': (ListTags, ARGS_NONE,
357
                [DEBUG_OPT], "", "List the tags of the cluster"),
358
  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
359
               "tag...", "Add tags to the cluster"),
360
  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
361
                  "tag...", "Remove tags from the cluster"),
362
  'search-tags': (SearchTags, ARGS_ONE,
363
                  [DEBUG_OPT], "", "Searches the tags on all objects on"
364
                  " the cluster for a given pattern (regex)"),
365
  }
366

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