Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 79f87a76

History | View | Annotate | Download (6.8 kB)

1
#!/usr/bin/python
2
#
3

    
4
import sys
5
import optparse
6

    
7
from ganeti import opcodes
8
from ganeti import mcpu
9
from ganeti import objects
10
from ganeti import constants
11
from ganeti import cli
12
from ganeti import logger
13
from ganeti import errors
14
from ganeti import utils
15

    
16
USAGE = ("\tburnin -o OS_NAME [options...] instance_name ...")
17

    
18
def Usage():
19
  """Shows program usage information and exits the program."""
20

    
21
  print >> sys.stderr, "Usage:"
22
  print >> sys.stderr, USAGE
23
  sys.exit(2)
24

    
25

    
26
def Feedback(msg):
27
  """Simple function that prints out its argument.
28

    
29
  """
30
  print msg
31

    
32

    
33
def ParseOptions():
34
  """Parses the command line options.
35

    
36
  In case of command line errors, it will show the usage and exit the
37
  program.
38

    
39
  Returns:
40
    (options, args), as returned by OptionParser.parse_args
41
  """
42

    
43
  parser = optparse.OptionParser(usage="\n%s" % USAGE,
44
                                 version="%%prog (ganeti) %s" %
45
                                 constants.RELEASE_VERSION,
46
                                 option_class=cli.CliOption)
47

    
48
  parser.add_option("-o", "--os", dest="os", default=None,
49
                    help="OS to use during burnin",
50
                    metavar="<OS>")
51
  parser.add_option("--os-size", dest="os_size", help="Disk size",
52
                    default=4 * 1024, type="unit", metavar="<size>")
53
  parser.add_option("--swap-size", dest="swap_size", help="Swap size",
54
                    default=4 * 1024, type="unit", metavar="<size>")
55
  parser.add_option("-v", "--verbose",
56
                    action="store_true", dest="verbose", default=False,
57
                    help="print command execution messages to stdout")
58
  parser.add_option("--no-replace1", dest="do_replace1",
59
                    help="Do disk replacement with the same secondary",
60
                    action="store_false", default=True)
61
  parser.add_option("--no-replace2", dest="do_replace2",
62
                    help="Do disk replacement with a different secondary",
63
                    action="store_false", default=True)
64
  parser.add_option("--no-failover", dest="do_failover",
65
                    help="Do instance failovers", action="store_false",
66
                    default=True)
67
  parser.add_option("-t", "--disk-template", dest="disk_template",
68
                    choices=("remote_raid1", "drbd8"), default="remote_raid1",
69
                    help="Template type for network mirroring (remote_raid1"
70
                    " or drbd8) [remote_raid1]")
71

    
72
  options, args = parser.parse_args()
73
  if len(args) < 1 or options.os is None:
74
    Usage()
75

    
76
  return options, args
77

    
78

    
79
def BurninCluster(opts, args):
80
  """Test a cluster intensively.
81

    
82
  This will create instances and then start/stop/failover them.
83
  It is safe for existing instances but could impact performance.
84

    
85
  """
86

    
87
  logger.SetupLogging(debug=True, program="ganeti/burnin")
88
  proc = mcpu.Processor(feedback=Feedback)
89
  result = proc.ExecOpCode(opcodes.OpQueryNodes(output_fields=["name"],
90
                                                names=[]))
91
  nodelist = [data[0] for data in result]
92

    
93
  Feedback("- Testing global parameters")
94

    
95
  result = proc.ExecOpCode(opcodes.OpDiagnoseOS())
96

    
97
  if not result:
98
    Feedback("Can't get the OS list")
99
    return 1
100

    
101
  # filter non-valid OS-es
102
  oses = {}
103
  for node_name in result:
104
    oses[node_name] = [obj for obj in result[node_name]
105
                       if isinstance(obj, objects.OS)]
106

    
107
  fnode = oses.keys()[0]
108
  os_set = set([os_inst.name for os_inst in oses[fnode]])
109
  del oses[fnode]
110
  for node in oses:
111
    os_set &= set([os_inst.name for os_inst in oses[node]])
112

    
113
  if opts.os not in os_set:
114
    Feedback("OS '%s' not found" % opts.os)
115
    return 1
116

    
117
  to_remove = []
118
  if opts.disk_template == "remote_raid1":
119
    disk_template = constants.DT_REMOTE_RAID1
120
  elif opts.disk_template == "drbd8":
121
    disk_template = constants.DT_DRBD8
122
  else:
123
    Feedback("Unknown disk template '%s'" % opts.disk_template)
124
    return 1
125
  try:
126
    idx = 0
127
    for instance_name in args:
128
      next_idx = idx + 1
129
      if next_idx >= len(nodelist):
130
        next_idx = 0
131
      pnode = nodelist[idx]
132
      snode = nodelist[next_idx]
133
      if len(nodelist) > 1:
134
        tplate = disk_template
135
      else:
136
        tplate = constants.DT_PLAIN
137

    
138
      op = opcodes.OpCreateInstance(instance_name=instance_name, mem_size=128,
139
                                    disk_size=opts.os_size,
140
                                    swap_size=opts.swap_size,
141
                                    disk_template=tplate,
142
                                    mode=constants.INSTANCE_CREATE,
143
                                    os_type=opts.os, pnode=pnode,
144
                                    snode=snode, vcpus=1,
145
                                    start=True,
146
                                    ip_check=True,
147
                                    wait_for_sync=True)
148
      Feedback("- Add instance %s on node %s" % (instance_name, pnode))
149
      result = proc.ExecOpCode(op)
150
      to_remove.append(instance_name)
151
      idx = next_idx
152

    
153

    
154
    if opts.do_replace1:
155
      if len(nodelist) > 1:
156
        # failover
157
        for instance_name in args:
158
          op = opcodes.OpReplaceDisks(instance_name=instance_name,
159
                                      remote_node=None,
160
                                      mode=constants.REPLACE_DISK_ALL,
161
                                      disks=["sda", "sdb"])
162

    
163
          Feedback("- Replace disks for instance %s" % (instance_name))
164
          result = proc.ExecOpCode(op)
165
      else:
166
        Feedback("- Can't run replace1, not enough nodes")
167

    
168
    if opts.do_failover:
169
      if len(nodelist) > 1:
170
        # failover
171
        for instance_name in args:
172
          op = opcodes.OpFailoverInstance(instance_name=instance_name,
173
                                          ignore_consistency=True)
174

    
175
          Feedback("- Failover instance %s" % (instance_name))
176
          result = proc.ExecOpCode(op)
177
      else:
178
        Feedback("- Can't run failovers, not enough nodes")
179

    
180
    # stop / start
181
    for instance_name in args:
182
      op = opcodes.OpShutdownInstance(instance_name=instance_name)
183
      Feedback("- Shutdown instance %s" % instance_name)
184
      result = proc.ExecOpCode(op)
185
      op = opcodes.OpStartupInstance(instance_name=instance_name, force=False)
186
      Feedback("- Start instance %s" % instance_name)
187
      result = proc.ExecOpCode(op)
188

    
189
  finally:
190
    # remove
191
    for instance_name in to_remove:
192
      op = opcodes.OpRemoveInstance(instance_name=instance_name)
193
      Feedback("- Remove instance %s" % instance_name)
194
      result = proc.ExecOpCode(op)
195

    
196
  return 0
197

    
198
def main():
199
  """Main function"""
200

    
201
  opts, args = ParseOptions()
202
  try:
203
    utils.Lock('cmd', max_retries=15, debug=True)
204
  except errors.LockError, err:
205
    logger.ToStderr(str(err))
206
    return 1
207
  try:
208
    retval = BurninCluster(opts, args)
209
  finally:
210
    utils.Unlock('cmd')
211
    utils.LockCleanup()
212
  return retval
213

    
214
if __name__ == "__main__":
215
  main()