Fix tools/burnin w.r.t. OpCreateInstance.ip_check
[ganeti-local] / tools / burnin
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("--do-replace1", dest="do_replace1",
59                     help="Do disk replacement with the same secondary",
60                     action="store_false", default=True)
61   parser.add_option("--do-replace2", dest="do_replace2",
62                     help="Do disk replacement with a different secondary",
63                     action="store_false", default=True)
64   parser.add_option("--do-failover", dest="do_failover",
65                     help="Do instance failovers", action="store_false",
66                     default=True)
67
68   options, args = parser.parse_args()
69   if len(args) < 1 or options.os is None:
70     Usage()
71
72   return options, args
73
74
75 def BurninCluster(opts, args):
76   """Test a cluster intensively.
77
78   This will create instances and then start/stop/failover them.
79   It is safe for existing instances but could impact performance.
80
81   """
82
83   logger.SetupLogging(debug=True, program="ganeti/burnin")
84   proc = mcpu.Processor()
85   result = proc.ExecOpCode(opcodes.OpQueryNodes(output_fields=["name"],
86                                                 names=[]), Feedback)
87   nodelist = [data[0] for data in result]
88
89   Feedback("- Testing global parameters")
90
91   result = proc.ExecOpCode(opcodes.OpDiagnoseOS(), Feedback)
92
93   if not result:
94     Feedback("Can't get the OS list")
95     return 1
96
97   # filter non-valid OS-es
98   oses = {}
99   for node_name in result:
100     oses[node_name] = [obj for obj in result[node_name]
101                        if isinstance(obj, objects.OS)]
102
103   fnode = oses.keys()[0]
104   os_set = set([os_inst.name for os_inst in oses[fnode]])
105   del oses[fnode]
106   for node in oses:
107     os_set &= set([os_inst.name for os_inst in oses[node]])
108
109   if opts.os not in os_set:
110     Feedback("OS '%s' not found" % opts.os)
111     return 1
112
113   to_remove = []
114   try:
115     idx = 0
116     for instance_name in args:
117       next_idx = idx + 1
118       if next_idx >= len(nodelist):
119         next_idx = 0
120       pnode = nodelist[idx]
121       snode = nodelist[next_idx]
122       if len(nodelist) > 1:
123         tplate = constants.DT_REMOTE_RAID1
124       else:
125         tplate = constants.DT_PLAIN
126
127       op = opcodes.OpCreateInstance(instance_name=instance_name, mem_size=128,
128                                     disk_size=opts.os_size,
129                                     swap_size=opts.swap_size,
130                                     disk_template=tplate,
131                                     mode=constants.INSTANCE_CREATE,
132                                     os_type=opts.os, pnode=pnode,
133                                     snode=snode, vcpus=1,
134                                     start=True,
135                                     ip_check=True,
136                                     wait_for_sync=True)
137       Feedback("- Add instance %s on node %s" % (instance_name, pnode))
138       result = proc.ExecOpCode(op, Feedback)
139       to_remove.append(instance_name)
140       idx = next_idx
141
142
143     if opts.do_replace1:
144       if len(nodelist) > 1:
145         # failover
146         for instance_name in args:
147           op = opcodes.OpReplaceDisks(instance_name=instance_name,
148                                       remote_node=None)
149
150           Feedback("- Replace disks for instance %s" % (instance_name))
151           result = proc.ExecOpCode(op, Feedback)
152       else:
153         Feedback("- Can't run replace1, not enough nodes")
154
155     if opts.do_failover:
156       if len(nodelist) > 1:
157         # failover
158         for instance_name in args:
159           op = opcodes.OpFailoverInstance(instance_name=instance_name,
160                                           ignore_consistency=True)
161
162           Feedback("- Failover instance %s" % (instance_name))
163           result = proc.ExecOpCode(op, Feedback)
164       else:
165         Feedback("- Can't run failovers, not enough nodes")
166
167     # stop / start
168     for instance_name in args:
169       op = opcodes.OpShutdownInstance(instance_name=instance_name)
170       Feedback("- Shutdown instance %s" % instance_name)
171       result = proc.ExecOpCode(op, Feedback)
172       op = opcodes.OpStartupInstance(instance_name=instance_name, force=False)
173       Feedback("- Start instance %s" % instance_name)
174       result = proc.ExecOpCode(op, Feedback)
175
176   finally:
177     # remove
178     for instance_name in to_remove:
179       op = opcodes.OpRemoveInstance(instance_name=instance_name)
180       Feedback("- Remove instance %s" % instance_name)
181       result = proc.ExecOpCode(op, Feedback)
182
183   return 0
184
185 def main():
186   """Main function"""
187
188   opts, args = ParseOptions()
189   try:
190     utils.Lock('cmd', max_retries=15, debug=True)
191   except errors.LockError, err:
192     logger.ToStderr(str(err))
193     return 1
194   try:
195     retval = BurninCluster(opts, args)
196   finally:
197     utils.Unlock('cmd')
198     utils.LockCleanup()
199   return retval
200
201 if __name__ == "__main__":
202   main()