Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ b63ed789

History | View | Annotate | Download (11.8 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
  SubmitOpCode(op)
49
  return 0
50

    
51

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

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

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

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

    
68

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

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

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

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

    
90

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

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

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

    
107

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

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

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

    
120

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

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

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

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

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

    
135
  return 0
136

    
137

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

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

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

    
152

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

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

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

    
173

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

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

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

    
185

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

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

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

    
198
  nodes, nlvm, instances, missing = result
199

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

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

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

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

    
245
  return retcode
246

    
247

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

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

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

    
259

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

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

    
273

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

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

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