Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 6e06b36c

History | View | Annotate | Download (10.7 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 01b69ec5 Michael Hanselmann
# Copyright (C) 2006, 2007 Google Inc.
5 01b69ec5 Michael Hanselmann
#
6 01b69ec5 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 01b69ec5 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 01b69ec5 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 01b69ec5 Michael Hanselmann
# (at your option) any later version.
10 01b69ec5 Michael Hanselmann
#
11 01b69ec5 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 01b69ec5 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 01b69ec5 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 01b69ec5 Michael Hanselmann
# General Public License for more details.
15 01b69ec5 Michael Hanselmann
#
16 01b69ec5 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 01b69ec5 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 01b69ec5 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 01b69ec5 Michael Hanselmann
# 02110-1301, USA.
20 01b69ec5 Michael Hanselmann
21 01b69ec5 Michael Hanselmann
22 01b69ec5 Michael Hanselmann
"""Burnin program
23 01b69ec5 Michael Hanselmann
24 01b69ec5 Michael Hanselmann
"""
25 175f44c2 Iustin Pop
26 a8083063 Iustin Pop
import sys
27 a8083063 Iustin Pop
import optparse
28 175f44c2 Iustin Pop
from itertools import izip, islice, cycle
29 21546b1c Iustin Pop
from cStringIO import StringIO
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import opcodes
32 a8083063 Iustin Pop
from ganeti import mcpu
33 a8083063 Iustin Pop
from ganeti import constants
34 a8083063 Iustin Pop
from ganeti import cli
35 a8083063 Iustin Pop
from ganeti import logger
36 9f13fc7a Iustin Pop
from ganeti import errors
37 9f13fc7a Iustin Pop
from ganeti import utils
38 a8083063 Iustin Pop
39 01b69ec5 Michael Hanselmann
40 9f13fc7a Iustin Pop
USAGE = ("\tburnin -o OS_NAME [options...] instance_name ...")
41 a8083063 Iustin Pop
42 01b69ec5 Michael Hanselmann
43 a8083063 Iustin Pop
def Usage():
44 a8083063 Iustin Pop
  """Shows program usage information and exits the program."""
45 a8083063 Iustin Pop
46 a8083063 Iustin Pop
  print >> sys.stderr, "Usage:"
47 a8083063 Iustin Pop
  print >> sys.stderr, USAGE
48 a8083063 Iustin Pop
  sys.exit(2)
49 a8083063 Iustin Pop
50 01b69ec5 Michael Hanselmann
51 21546b1c Iustin Pop
def Log(msg):
52 3ecf6786 Iustin Pop
  """Simple function that prints out its argument.
53 3ecf6786 Iustin Pop
54 3ecf6786 Iustin Pop
  """
55 3ecf6786 Iustin Pop
  print msg
56 84cc52ab Michael Hanselmann
  sys.stdout.flush()
57 a8083063 Iustin Pop
58 01b69ec5 Michael Hanselmann
59 175f44c2 Iustin Pop
class Burner(object):
60 175f44c2 Iustin Pop
  """Burner class."""
61 175f44c2 Iustin Pop
62 175f44c2 Iustin Pop
  def __init__(self):
63 175f44c2 Iustin Pop
    """Constructor."""
64 21546b1c Iustin Pop
    logger.SetupLogging(debug=False, program="ganeti/burnin")
65 21546b1c Iustin Pop
    self._feed_buf = StringIO()
66 21546b1c Iustin Pop
    self.proc = mcpu.Processor(feedback=self.Feedback)
67 175f44c2 Iustin Pop
    self.nodes = []
68 175f44c2 Iustin Pop
    self.instances = []
69 175f44c2 Iustin Pop
    self.to_rem = []
70 175f44c2 Iustin Pop
    self.opts = None
71 175f44c2 Iustin Pop
    self.ParseOptions()
72 175f44c2 Iustin Pop
    self.GetState()
73 175f44c2 Iustin Pop
74 21546b1c Iustin Pop
  def ClearFeedbackBuf(self):
75 21546b1c Iustin Pop
    """Clear the feedback buffer."""
76 21546b1c Iustin Pop
    self._feed_buf.truncate(0)
77 21546b1c Iustin Pop
78 21546b1c Iustin Pop
  def GetFeedbackBuf(self):
79 21546b1c Iustin Pop
    """Return the contents of the buffer."""
80 21546b1c Iustin Pop
    return self._feed_buf.getvalue()
81 21546b1c Iustin Pop
82 21546b1c Iustin Pop
  def Feedback(self, msg):
83 21546b1c Iustin Pop
    """Acumulate feedback in our buffer."""
84 21546b1c Iustin Pop
    self._feed_buf.write(msg)
85 21546b1c Iustin Pop
    self._feed_buf.write("\n")
86 21546b1c Iustin Pop
87 21546b1c Iustin Pop
  def ExecOp(self, op):
88 21546b1c Iustin Pop
    """Execute an opcode and manage the exec buffer."""
89 21546b1c Iustin Pop
    self.ClearFeedbackBuf()
90 21546b1c Iustin Pop
    return self.proc.ExecOpCode(op)
91 21546b1c Iustin Pop
92 175f44c2 Iustin Pop
  def ParseOptions(self):
93 175f44c2 Iustin Pop
    """Parses the command line options.
94 175f44c2 Iustin Pop
95 175f44c2 Iustin Pop
    In case of command line errors, it will show the usage and exit the
96 175f44c2 Iustin Pop
    program.
97 175f44c2 Iustin Pop
98 175f44c2 Iustin Pop
    """
99 175f44c2 Iustin Pop
100 175f44c2 Iustin Pop
    parser = optparse.OptionParser(usage="\n%s" % USAGE,
101 175f44c2 Iustin Pop
                                   version="%%prog (ganeti) %s" %
102 175f44c2 Iustin Pop
                                   constants.RELEASE_VERSION,
103 175f44c2 Iustin Pop
                                   option_class=cli.CliOption)
104 175f44c2 Iustin Pop
105 175f44c2 Iustin Pop
    parser.add_option("-o", "--os", dest="os", default=None,
106 175f44c2 Iustin Pop
                      help="OS to use during burnin",
107 175f44c2 Iustin Pop
                      metavar="<OS>")
108 175f44c2 Iustin Pop
    parser.add_option("--os-size", dest="os_size", help="Disk size",
109 175f44c2 Iustin Pop
                      default=4 * 1024, type="unit", metavar="<size>")
110 175f44c2 Iustin Pop
    parser.add_option("--swap-size", dest="swap_size", help="Swap size",
111 175f44c2 Iustin Pop
                      default=4 * 1024, type="unit", metavar="<size>")
112 175f44c2 Iustin Pop
    parser.add_option("-v", "--verbose",
113 175f44c2 Iustin Pop
                      action="store_true", dest="verbose", default=False,
114 175f44c2 Iustin Pop
                      help="print command execution messages to stdout")
115 175f44c2 Iustin Pop
    parser.add_option("--no-replace1", dest="do_replace1",
116 175f44c2 Iustin Pop
                      help="Skip disk replacement with the same secondary",
117 175f44c2 Iustin Pop
                      action="store_false", default=True)
118 175f44c2 Iustin Pop
    parser.add_option("--no-replace2", dest="do_replace2",
119 175f44c2 Iustin Pop
                      help="Skip disk replacement with a different secondary",
120 175f44c2 Iustin Pop
                      action="store_false", default=True)
121 175f44c2 Iustin Pop
    parser.add_option("--no-failover", dest="do_failover",
122 175f44c2 Iustin Pop
                      help="Skip instance failovers", action="store_false",
123 175f44c2 Iustin Pop
                      default=True)
124 175f44c2 Iustin Pop
    parser.add_option("-t", "--disk-template", dest="disk_template",
125 12c3449a Michael Hanselmann
                      choices=("remote_raid1", "drbd"),
126 175f44c2 Iustin Pop
                      default="remote_raid1",
127 175f44c2 Iustin Pop
                      help="Template type for network mirroring (remote_raid1"
128 12c3449a Michael Hanselmann
                      " or drbd) [remote_raid1]")
129 175f44c2 Iustin Pop
    parser.add_option("-n", "--nodes", dest="nodes", default="",
130 175f44c2 Iustin Pop
                      help="Comma separated list of nodes to perform"
131 175f44c2 Iustin Pop
                      " the burnin on (defaults to all nodes)")
132 175f44c2 Iustin Pop
133 175f44c2 Iustin Pop
    options, args = parser.parse_args()
134 175f44c2 Iustin Pop
    if len(args) < 1 or options.os is None:
135 175f44c2 Iustin Pop
      Usage()
136 175f44c2 Iustin Pop
137 12c3449a Michael Hanselmann
    supported_disk_templates = (constants.DT_PLAIN, constants.DT_REMOTE_RAID1,
138 12c3449a Michael Hanselmann
                                constants.DT_DRBD8)
139 12c3449a Michael Hanselmann
    if options.disk_template not in supported_disk_templates:
140 21546b1c Iustin Pop
      Log("Unknown disk template '%s'" % options.disk_template)
141 175f44c2 Iustin Pop
      sys.exit(1)
142 175f44c2 Iustin Pop
143 175f44c2 Iustin Pop
    self.opts = options
144 175f44c2 Iustin Pop
    self.instances = args
145 175f44c2 Iustin Pop
146 175f44c2 Iustin Pop
  def GetState(self):
147 175f44c2 Iustin Pop
    """Read the cluster state from the config."""
148 175f44c2 Iustin Pop
    if self.opts.nodes:
149 175f44c2 Iustin Pop
      names = self.opts.nodes.split(",")
150 175f44c2 Iustin Pop
    else:
151 175f44c2 Iustin Pop
      names = []
152 175f44c2 Iustin Pop
    try:
153 175f44c2 Iustin Pop
      op = opcodes.OpQueryNodes(output_fields=["name"], names=names)
154 21546b1c Iustin Pop
      result = self.ExecOp(op)
155 175f44c2 Iustin Pop
    except errors.GenericError, err:
156 175f44c2 Iustin Pop
      err_code, msg = cli.FormatError(err)
157 21546b1c Iustin Pop
      Log(msg)
158 175f44c2 Iustin Pop
      sys.exit(err_code)
159 175f44c2 Iustin Pop
    self.nodes = [data[0] for data in result]
160 175f44c2 Iustin Pop
161 21546b1c Iustin Pop
    result = self.ExecOp(opcodes.OpDiagnoseOS())
162 175f44c2 Iustin Pop
163 175f44c2 Iustin Pop
    if not result:
164 21546b1c Iustin Pop
      Log("Can't get the OS list")
165 175f44c2 Iustin Pop
      sys.exit(1)
166 175f44c2 Iustin Pop
167 175f44c2 Iustin Pop
    # filter non-valid OS-es
168 175f44c2 Iustin Pop
    oses = {}
169 175f44c2 Iustin Pop
    for node_name in result:
170 175f44c2 Iustin Pop
      oses[node_name] = [obj for obj in result[node_name] if obj]
171 175f44c2 Iustin Pop
172 175f44c2 Iustin Pop
    fnode = oses.keys()[0]
173 175f44c2 Iustin Pop
    os_set = set([os_inst.name for os_inst in oses[fnode]])
174 175f44c2 Iustin Pop
    del oses[fnode]
175 175f44c2 Iustin Pop
    for node in oses:
176 175f44c2 Iustin Pop
      os_set &= set([os_inst.name for os_inst in oses[node]])
177 175f44c2 Iustin Pop
178 175f44c2 Iustin Pop
    if self.opts.os not in os_set:
179 21546b1c Iustin Pop
      Log("OS '%s' not found" % self.opts.os)
180 175f44c2 Iustin Pop
      sys.exit(1)
181 175f44c2 Iustin Pop
182 175f44c2 Iustin Pop
  def CreateInstances(self):
183 175f44c2 Iustin Pop
    """Create the given instances.
184 175f44c2 Iustin Pop
185 175f44c2 Iustin Pop
    """
186 175f44c2 Iustin Pop
    self.to_rem = []
187 175f44c2 Iustin Pop
    mytor = izip(cycle(self.nodes),
188 175f44c2 Iustin Pop
                 islice(cycle(self.nodes), 1, None),
189 175f44c2 Iustin Pop
                 self.instances)
190 175f44c2 Iustin Pop
    for pnode, snode, instance in mytor:
191 175f44c2 Iustin Pop
      op = opcodes.OpCreateInstance(instance_name=instance,
192 175f44c2 Iustin Pop
                                    mem_size=128,
193 175f44c2 Iustin Pop
                                    disk_size=self.opts.os_size,
194 175f44c2 Iustin Pop
                                    swap_size=self.opts.swap_size,
195 175f44c2 Iustin Pop
                                    disk_template=self.opts.disk_template,
196 a8083063 Iustin Pop
                                    mode=constants.INSTANCE_CREATE,
197 175f44c2 Iustin Pop
                                    os_type=self.opts.os,
198 175f44c2 Iustin Pop
                                    pnode=pnode,
199 175f44c2 Iustin Pop
                                    snode=snode,
200 175f44c2 Iustin Pop
                                    vcpus=1,
201 a8083063 Iustin Pop
                                    start=True,
202 e9f745aa Iustin Pop
                                    ip_check=True,
203 a8083063 Iustin Pop
                                    wait_for_sync=True)
204 21546b1c Iustin Pop
      Log("- Add instance %s on node %s" % (instance, pnode))
205 21546b1c Iustin Pop
      self.ExecOp(op)
206 175f44c2 Iustin Pop
      self.to_rem.append(instance)
207 175f44c2 Iustin Pop
208 175f44c2 Iustin Pop
  def ReplaceDisks1R1(self):
209 175f44c2 Iustin Pop
    """Replace disks with the same secondary for rr1."""
210 175f44c2 Iustin Pop
    # replace all, both disks
211 175f44c2 Iustin Pop
    for instance in self.instances:
212 175f44c2 Iustin Pop
      op = opcodes.OpReplaceDisks(instance_name=instance,
213 175f44c2 Iustin Pop
                                  remote_node=None,
214 175f44c2 Iustin Pop
                                  mode=constants.REPLACE_DISK_ALL,
215 175f44c2 Iustin Pop
                                  disks=["sda", "sdb"])
216 175f44c2 Iustin Pop
217 21546b1c Iustin Pop
      Log("- Replace disks for instance %s" % (instance))
218 21546b1c Iustin Pop
      self.ExecOp(op)
219 175f44c2 Iustin Pop
220 175f44c2 Iustin Pop
  def ReplaceDisks1D8(self):
221 175f44c2 Iustin Pop
    """Replace disks on primary and secondary for drbd8."""
222 175f44c2 Iustin Pop
    for instance in self.instances:
223 175f44c2 Iustin Pop
      for mode in constants.REPLACE_DISK_SEC, constants.REPLACE_DISK_PRI:
224 175f44c2 Iustin Pop
        op = opcodes.OpReplaceDisks(instance_name=instance,
225 175f44c2 Iustin Pop
                                    mode=mode,
226 175f44c2 Iustin Pop
                                    disks=["sda", "sdb"])
227 21546b1c Iustin Pop
        Log("- Replace disks (%s) for instance %s" % (mode, instance))
228 21546b1c Iustin Pop
        self.ExecOp(op)
229 175f44c2 Iustin Pop
230 175f44c2 Iustin Pop
  def ReplaceDisks2(self):
231 175f44c2 Iustin Pop
    """Replace secondary node."""
232 175f44c2 Iustin Pop
    if self.opts.disk_template == constants.DT_REMOTE_RAID1:
233 175f44c2 Iustin Pop
      mode = constants.REPLACE_DISK_ALL
234 175f44c2 Iustin Pop
    else:
235 175f44c2 Iustin Pop
      mode = constants.REPLACE_DISK_SEC
236 175f44c2 Iustin Pop
237 175f44c2 Iustin Pop
    mytor = izip(islice(cycle(self.nodes), 2, None),
238 175f44c2 Iustin Pop
                 self.instances)
239 175f44c2 Iustin Pop
    for tnode, instance in mytor:
240 175f44c2 Iustin Pop
      op = opcodes.OpReplaceDisks(instance_name=instance,
241 175f44c2 Iustin Pop
                                  mode=mode,
242 175f44c2 Iustin Pop
                                  remote_node=tnode,
243 175f44c2 Iustin Pop
                                  disks=["sda", "sdb"])
244 21546b1c Iustin Pop
      Log("- Replace secondary (%s) for instance %s" % (mode, instance))
245 21546b1c Iustin Pop
      self.ExecOp(op)
246 175f44c2 Iustin Pop
247 175f44c2 Iustin Pop
  def Failover(self):
248 175f44c2 Iustin Pop
    """Failover the instances."""
249 175f44c2 Iustin Pop
250 175f44c2 Iustin Pop
    for instance in self.instances:
251 175f44c2 Iustin Pop
      op = opcodes.OpFailoverInstance(instance_name=instance,
252 175f44c2 Iustin Pop
                                      ignore_consistency=False)
253 175f44c2 Iustin Pop
254 21546b1c Iustin Pop
      Log("- Failover instance %s" % (instance))
255 21546b1c Iustin Pop
      self.ExecOp(op)
256 175f44c2 Iustin Pop
257 175f44c2 Iustin Pop
  def StopStart(self):
258 175f44c2 Iustin Pop
    """Stop/start the instances."""
259 175f44c2 Iustin Pop
    for instance in self.instances:
260 175f44c2 Iustin Pop
      op = opcodes.OpShutdownInstance(instance_name=instance)
261 21546b1c Iustin Pop
      Log("- Shutdown instance %s" % instance)
262 21546b1c Iustin Pop
      self.ExecOp(op)
263 175f44c2 Iustin Pop
      op = opcodes.OpStartupInstance(instance_name=instance, force=False)
264 21546b1c Iustin Pop
      Log("- Start instance %s" % instance)
265 21546b1c Iustin Pop
      self.ExecOp(op)
266 175f44c2 Iustin Pop
267 175f44c2 Iustin Pop
  def Remove(self):
268 175f44c2 Iustin Pop
    """Remove the instances."""
269 175f44c2 Iustin Pop
    for instance in self.to_rem:
270 175f44c2 Iustin Pop
      op = opcodes.OpRemoveInstance(instance_name=instance)
271 21546b1c Iustin Pop
      Log("- Remove instance %s" % instance)
272 21546b1c Iustin Pop
      self.ExecOp(op)
273 175f44c2 Iustin Pop
274 175f44c2 Iustin Pop
  def BurninCluster(self):
275 175f44c2 Iustin Pop
    """Test a cluster intensively.
276 175f44c2 Iustin Pop
277 175f44c2 Iustin Pop
    This will create instances and then start/stop/failover them.
278 175f44c2 Iustin Pop
    It is safe for existing instances but could impact performance.
279 175f44c2 Iustin Pop
280 175f44c2 Iustin Pop
    """
281 175f44c2 Iustin Pop
282 175f44c2 Iustin Pop
    opts = self.opts
283 175f44c2 Iustin Pop
284 21546b1c Iustin Pop
    Log("- Testing global parameters")
285 175f44c2 Iustin Pop
286 175f44c2 Iustin Pop
    if len(self.nodes) == 1 and opts.disk_template != constants.DT_PLAIN:
287 21546b1c Iustin Pop
      Log("When one node is available/selected the disk template must"
288 175f44c2 Iustin Pop
               " be 'plain'")
289 175f44c2 Iustin Pop
      sys.exit(1)
290 175f44c2 Iustin Pop
291 21546b1c Iustin Pop
    has_err = True
292 175f44c2 Iustin Pop
    try:
293 175f44c2 Iustin Pop
      self.CreateInstances()
294 175f44c2 Iustin Pop
      if opts.do_replace1 and opts.disk_template in constants.DTS_NET_MIRROR:
295 175f44c2 Iustin Pop
        if opts.disk_template == constants.DT_REMOTE_RAID1:
296 175f44c2 Iustin Pop
          self.ReplaceDisks1R1()
297 175f44c2 Iustin Pop
        elif opts.disk_template == constants.DT_DRBD8:
298 175f44c2 Iustin Pop
          self.ReplaceDisks1D8()
299 175f44c2 Iustin Pop
      if (opts.do_replace2 and len(self.nodes) > 2 and
300 175f44c2 Iustin Pop
          opts.disk_template in constants.DTS_NET_MIRROR) :
301 175f44c2 Iustin Pop
        self.ReplaceDisks2()
302 175f44c2 Iustin Pop
303 175f44c2 Iustin Pop
      if opts.do_failover and opts.disk_template in constants.DTS_NET_MIRROR:
304 175f44c2 Iustin Pop
        self.Failover()
305 175f44c2 Iustin Pop
306 175f44c2 Iustin Pop
      self.StopStart()
307 21546b1c Iustin Pop
      has_err = False
308 175f44c2 Iustin Pop
    finally:
309 21546b1c Iustin Pop
      if has_err:
310 21546b1c Iustin Pop
        Log("Error detected: opcode buffer follows:\n\n")
311 21546b1c Iustin Pop
        Log(self.GetFeedbackBuf())
312 21546b1c Iustin Pop
        Log("\n\n")
313 175f44c2 Iustin Pop
      self.Remove()
314 175f44c2 Iustin Pop
315 175f44c2 Iustin Pop
    return 0
316 a8083063 Iustin Pop
317 01b69ec5 Michael Hanselmann
318 a8083063 Iustin Pop
def main():
319 3ecf6786 Iustin Pop
  """Main function"""
320 3ecf6786 Iustin Pop
321 175f44c2 Iustin Pop
  burner = Burner()
322 3ecf6786 Iustin Pop
  try:
323 3ecf6786 Iustin Pop
    utils.Lock('cmd', max_retries=15, debug=True)
324 3ecf6786 Iustin Pop
  except errors.LockError, err:
325 3ecf6786 Iustin Pop
    logger.ToStderr(str(err))
326 3ecf6786 Iustin Pop
    return 1
327 3ecf6786 Iustin Pop
  try:
328 175f44c2 Iustin Pop
    retval = burner.BurninCluster()
329 3ecf6786 Iustin Pop
  finally:
330 3ecf6786 Iustin Pop
    utils.Unlock('cmd')
331 3ecf6786 Iustin Pop
    utils.LockCleanup()
332 3ecf6786 Iustin Pop
  return retval
333 a8083063 Iustin Pop
334 01b69ec5 Michael Hanselmann
335 a8083063 Iustin Pop
if __name__ == "__main__":
336 3ecf6786 Iustin Pop
  main()