Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 12c3449a

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