Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 5a9c3f46

History | View | Annotate | Download (20 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 bd5e77f9 Guido Trotter
import os
27 a8083063 Iustin Pop
import sys
28 a8083063 Iustin Pop
import optparse
29 e2212007 Iustin Pop
import time
30 175f44c2 Iustin Pop
from itertools import izip, islice, cycle
31 21546b1c Iustin Pop
from cStringIO import StringIO
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
from ganeti import opcodes
34 a8083063 Iustin Pop
from ganeti import mcpu
35 a8083063 Iustin Pop
from ganeti import constants
36 a8083063 Iustin Pop
from ganeti import cli
37 9f13fc7a Iustin Pop
from ganeti import errors
38 9f13fc7a Iustin Pop
from ganeti import utils
39 a8083063 Iustin Pop
40 01b69ec5 Michael Hanselmann
41 9f13fc7a Iustin Pop
USAGE = ("\tburnin -o OS_NAME [options...] instance_name ...")
42 a8083063 Iustin Pop
43 01b69ec5 Michael Hanselmann
44 a8083063 Iustin Pop
def Usage():
45 a8083063 Iustin Pop
  """Shows program usage information and exits the program."""
46 a8083063 Iustin Pop
47 a8083063 Iustin Pop
  print >> sys.stderr, "Usage:"
48 a8083063 Iustin Pop
  print >> sys.stderr, USAGE
49 a8083063 Iustin Pop
  sys.exit(2)
50 a8083063 Iustin Pop
51 01b69ec5 Michael Hanselmann
52 21546b1c Iustin Pop
def Log(msg):
53 3ecf6786 Iustin Pop
  """Simple function that prints out its argument.
54 3ecf6786 Iustin Pop
55 3ecf6786 Iustin Pop
  """
56 3ecf6786 Iustin Pop
  print msg
57 84cc52ab Michael Hanselmann
  sys.stdout.flush()
58 a8083063 Iustin Pop
59 01b69ec5 Michael Hanselmann
60 175f44c2 Iustin Pop
class Burner(object):
61 175f44c2 Iustin Pop
  """Burner class."""
62 175f44c2 Iustin Pop
63 175f44c2 Iustin Pop
  def __init__(self):
64 175f44c2 Iustin Pop
    """Constructor."""
65 82d9caef Iustin Pop
    utils.SetupLogging(constants.LOG_BURNIN, debug=False, stderr_logging=True)
66 21546b1c Iustin Pop
    self._feed_buf = StringIO()
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 ec5c88dc Iustin Pop
    self.cl = cli.GetClient()
72 175f44c2 Iustin Pop
    self.ParseOptions()
73 175f44c2 Iustin Pop
    self.GetState()
74 175f44c2 Iustin Pop
75 21546b1c Iustin Pop
  def ClearFeedbackBuf(self):
76 21546b1c Iustin Pop
    """Clear the feedback buffer."""
77 21546b1c Iustin Pop
    self._feed_buf.truncate(0)
78 21546b1c Iustin Pop
79 21546b1c Iustin Pop
  def GetFeedbackBuf(self):
80 21546b1c Iustin Pop
    """Return the contents of the buffer."""
81 21546b1c Iustin Pop
    return self._feed_buf.getvalue()
82 21546b1c Iustin Pop
83 21546b1c Iustin Pop
  def Feedback(self, msg):
84 21546b1c Iustin Pop
    """Acumulate feedback in our buffer."""
85 e17188f4 Iustin Pop
    self._feed_buf.write("%s %s\n" % (time.ctime(utils.MergeTime(msg[0])),
86 e17188f4 Iustin Pop
                                      msg[2]))
87 d7b47a77 Iustin Pop
    if self.opts.verbose:
88 d7b47a77 Iustin Pop
      Log(msg)
89 21546b1c Iustin Pop
90 21546b1c Iustin Pop
  def ExecOp(self, op):
91 21546b1c Iustin Pop
    """Execute an opcode and manage the exec buffer."""
92 21546b1c Iustin Pop
    self.ClearFeedbackBuf()
93 ec5c88dc Iustin Pop
    return cli.SubmitOpCode(op, feedback_fn=self.Feedback, cl=self.cl)
94 ec5c88dc Iustin Pop
95 ec5c88dc Iustin Pop
  def ExecJobSet(self, jobs):
96 ec5c88dc Iustin Pop
    """Execute a set of jobs and return once all are done.
97 ec5c88dc Iustin Pop
98 ec5c88dc Iustin Pop
    The method will return the list of results, if all jobs are
99 ec5c88dc Iustin Pop
    successfull. Otherwise, OpExecError will be raised from within
100 ec5c88dc Iustin Pop
    cli.py.
101 ec5c88dc Iustin Pop
102 ec5c88dc Iustin Pop
    """
103 ec5c88dc Iustin Pop
    self.ClearFeedbackBuf()
104 ec5c88dc Iustin Pop
    job_ids = [cli.SendJob(job, cl=self.cl) for job in jobs]
105 ec5c88dc Iustin Pop
    Log("- Submitted job IDs %s" % ", ".join(job_ids))
106 ec5c88dc Iustin Pop
    results = []
107 ec5c88dc Iustin Pop
    for jid in job_ids:
108 ec5c88dc Iustin Pop
      Log("- Waiting for job %s" % jid)
109 ec5c88dc Iustin Pop
      results.append(cli.PollJob(jid, cl=self.cl, feedback_fn=self.Feedback))
110 ec5c88dc Iustin Pop
111 ec5c88dc Iustin Pop
    return results
112 21546b1c Iustin Pop
113 175f44c2 Iustin Pop
  def ParseOptions(self):
114 175f44c2 Iustin Pop
    """Parses the command line options.
115 175f44c2 Iustin Pop
116 175f44c2 Iustin Pop
    In case of command line errors, it will show the usage and exit the
117 175f44c2 Iustin Pop
    program.
118 175f44c2 Iustin Pop
119 175f44c2 Iustin Pop
    """
120 175f44c2 Iustin Pop
121 175f44c2 Iustin Pop
    parser = optparse.OptionParser(usage="\n%s" % USAGE,
122 175f44c2 Iustin Pop
                                   version="%%prog (ganeti) %s" %
123 175f44c2 Iustin Pop
                                   constants.RELEASE_VERSION,
124 175f44c2 Iustin Pop
                                   option_class=cli.CliOption)
125 175f44c2 Iustin Pop
126 175f44c2 Iustin Pop
    parser.add_option("-o", "--os", dest="os", default=None,
127 175f44c2 Iustin Pop
                      help="OS to use during burnin",
128 175f44c2 Iustin Pop
                      metavar="<OS>")
129 08db7c5c Iustin Pop
    parser.add_option("--disk-size", dest="disk_size",
130 08db7c5c Iustin Pop
                      help="Disk size (determines disk count)",
131 08db7c5c Iustin Pop
                      default="128m", type="string", metavar="<size,size,...>")
132 08db7c5c Iustin Pop
    parser.add_option("--disk-growth", dest="disk_growth", help="Disk growth",
133 f768530c Iustin Pop
                      default="128m", type="string", metavar="<size,size,...>")
134 5e767b34 Iustin Pop
    parser.add_option("--mem-size", dest="mem_size", help="Memory size",
135 5e767b34 Iustin Pop
                      default=128, type="unit", metavar="<size>")
136 175f44c2 Iustin Pop
    parser.add_option("-v", "--verbose",
137 175f44c2 Iustin Pop
                      action="store_true", dest="verbose", default=False,
138 175f44c2 Iustin Pop
                      help="print command execution messages to stdout")
139 175f44c2 Iustin Pop
    parser.add_option("--no-replace1", dest="do_replace1",
140 175f44c2 Iustin Pop
                      help="Skip disk replacement with the same secondary",
141 175f44c2 Iustin Pop
                      action="store_false", default=True)
142 175f44c2 Iustin Pop
    parser.add_option("--no-replace2", dest="do_replace2",
143 175f44c2 Iustin Pop
                      help="Skip disk replacement with a different secondary",
144 175f44c2 Iustin Pop
                      action="store_false", default=True)
145 175f44c2 Iustin Pop
    parser.add_option("--no-failover", dest="do_failover",
146 175f44c2 Iustin Pop
                      help="Skip instance failovers", action="store_false",
147 175f44c2 Iustin Pop
                      default=True)
148 bd5e77f9 Guido Trotter
    parser.add_option("--no-importexport", dest="do_importexport",
149 bd5e77f9 Guido Trotter
                      help="Skip instance export/import", action="store_false",
150 bd5e77f9 Guido Trotter
                      default=True)
151 d4844f0f Guido Trotter
    parser.add_option("--no-startstop", dest="do_startstop",
152 d4844f0f Guido Trotter
                      help="Skip instance stop/start", action="store_false",
153 d4844f0f Guido Trotter
                      default=True)
154 00f91f29 Iustin Pop
    parser.add_option("--no-reinstall", dest="do_reinstall",
155 00f91f29 Iustin Pop
                      help="Skip instance reinstall", action="store_false",
156 00f91f29 Iustin Pop
                      default=True)
157 00f91f29 Iustin Pop
    parser.add_option("--no-reboot", dest="do_reboot",
158 00f91f29 Iustin Pop
                      help="Skip instance reboot", action="store_false",
159 00f91f29 Iustin Pop
                      default=True)
160 b518a14a Iustin Pop
    parser.add_option("--no-nics", dest="nics",
161 b518a14a Iustin Pop
                      help="No network interfaces", action="store_const",
162 b518a14a Iustin Pop
                      const=[], default=[{}])
163 054a8696 Manuel Franceschini
    parser.add_option("--rename", dest="rename", default=None,
164 054a8696 Manuel Franceschini
                      help="Give one unused instance name which is taken"
165 054a8696 Manuel Franceschini
                           " to start the renaming sequence",
166 054a8696 Manuel Franceschini
                      metavar="<instance_name>")
167 175f44c2 Iustin Pop
    parser.add_option("-t", "--disk-template", dest="disk_template",
168 2f505cb5 Manuel Franceschini
                      choices=("diskless", "file", "plain", "drbd"),
169 f9193417 Iustin Pop
                      default="drbd",
170 2f505cb5 Manuel Franceschini
                      help="Disk template (diskless, file, plain or drbd)"
171 2f505cb5 Manuel Franceschini
                            " [drbd]")
172 175f44c2 Iustin Pop
    parser.add_option("-n", "--nodes", dest="nodes", default="",
173 175f44c2 Iustin Pop
                      help="Comma separated list of nodes to perform"
174 175f44c2 Iustin Pop
                      " the burnin on (defaults to all nodes)")
175 b91bde14 Iustin Pop
    parser.add_option("--iallocator", dest="iallocator",
176 b91bde14 Iustin Pop
                      default=None, type="string",
177 b91bde14 Iustin Pop
                      help="Perform the allocation using an iallocator"
178 b91bde14 Iustin Pop
                      " instead of fixed node spread (node restrictions no"
179 b91bde14 Iustin Pop
                      " longer apply, therefore -n/--nodes must not be used")
180 ec5c88dc Iustin Pop
    parser.add_option("-p", "--parallel", default=False, action="store_true",
181 ec5c88dc Iustin Pop
                      dest="parallel",
182 ec5c88dc Iustin Pop
                      help="Enable parallelization of some operations in"
183 ec5c88dc Iustin Pop
                      " order to speed burnin or to test granular locking")
184 175f44c2 Iustin Pop
185 175f44c2 Iustin Pop
    options, args = parser.parse_args()
186 175f44c2 Iustin Pop
    if len(args) < 1 or options.os is None:
187 175f44c2 Iustin Pop
      Usage()
188 175f44c2 Iustin Pop
189 f9193417 Iustin Pop
    supported_disk_templates = (constants.DT_DISKLESS,
190 2f505cb5 Manuel Franceschini
                                constants.DT_FILE,
191 f9193417 Iustin Pop
                                constants.DT_PLAIN,
192 12c3449a Michael Hanselmann
                                constants.DT_DRBD8)
193 12c3449a Michael Hanselmann
    if options.disk_template not in supported_disk_templates:
194 21546b1c Iustin Pop
      Log("Unknown disk template '%s'" % options.disk_template)
195 175f44c2 Iustin Pop
      sys.exit(1)
196 175f44c2 Iustin Pop
197 b518a14a Iustin Pop
    if options.disk_template == constants.DT_DISKLESS:
198 b518a14a Iustin Pop
      disk_size = disk_growth = []
199 b518a14a Iustin Pop
    else:
200 b518a14a Iustin Pop
      disk_size = [utils.ParseUnit(v) for v in options.disk_size.split(",")]
201 b518a14a Iustin Pop
      disk_growth = [utils.ParseUnit(v)
202 b518a14a Iustin Pop
                     for v in options.disk_growth.split(",")]
203 b518a14a Iustin Pop
      if len(disk_growth) != len(disk_size):
204 b518a14a Iustin Pop
        Log("Wrong disk sizes/growth combination")
205 b518a14a Iustin Pop
        sys.exit(1)
206 08db7c5c Iustin Pop
    if ((disk_size and options.disk_template == constants.DT_DISKLESS) or
207 08db7c5c Iustin Pop
        (not disk_size and options.disk_template != constants.DT_DISKLESS)):
208 08db7c5c Iustin Pop
      Log("Wrong disk count/disk template combination")
209 08db7c5c Iustin Pop
      sys.exit(1)
210 08db7c5c Iustin Pop
211 08db7c5c Iustin Pop
    self.disk_size = disk_size
212 08db7c5c Iustin Pop
    self.disk_growth = disk_growth
213 08db7c5c Iustin Pop
    self.disk_count = len(disk_size)
214 08db7c5c Iustin Pop
215 b91bde14 Iustin Pop
    if options.nodes and options.iallocator:
216 b91bde14 Iustin Pop
      Log("Give either the nodes option or the iallocator option, not both")
217 b91bde14 Iustin Pop
      sys.exit(1)
218 b91bde14 Iustin Pop
219 175f44c2 Iustin Pop
    self.opts = options
220 175f44c2 Iustin Pop
    self.instances = args
221 338e51e8 Iustin Pop
    self.bep = {
222 338e51e8 Iustin Pop
      constants.BE_MEMORY: options.mem_size,
223 338e51e8 Iustin Pop
      constants.BE_VCPUS: 1,
224 338e51e8 Iustin Pop
      }
225 338e51e8 Iustin Pop
    self.hvp = {}
226 175f44c2 Iustin Pop
227 175f44c2 Iustin Pop
  def GetState(self):
228 175f44c2 Iustin Pop
    """Read the cluster state from the config."""
229 175f44c2 Iustin Pop
    if self.opts.nodes:
230 175f44c2 Iustin Pop
      names = self.opts.nodes.split(",")
231 175f44c2 Iustin Pop
    else:
232 175f44c2 Iustin Pop
      names = []
233 175f44c2 Iustin Pop
    try:
234 175f44c2 Iustin Pop
      op = opcodes.OpQueryNodes(output_fields=["name"], names=names)
235 21546b1c Iustin Pop
      result = self.ExecOp(op)
236 175f44c2 Iustin Pop
    except errors.GenericError, err:
237 175f44c2 Iustin Pop
      err_code, msg = cli.FormatError(err)
238 21546b1c Iustin Pop
      Log(msg)
239 175f44c2 Iustin Pop
      sys.exit(err_code)
240 175f44c2 Iustin Pop
    self.nodes = [data[0] for data in result]
241 175f44c2 Iustin Pop
242 1f9430d6 Iustin Pop
    result = self.ExecOp(opcodes.OpDiagnoseOS(output_fields=["name", "valid"],
243 1f9430d6 Iustin Pop
                                              names=[]))
244 175f44c2 Iustin Pop
245 175f44c2 Iustin Pop
    if not result:
246 21546b1c Iustin Pop
      Log("Can't get the OS list")
247 175f44c2 Iustin Pop
      sys.exit(1)
248 175f44c2 Iustin Pop
249 175f44c2 Iustin Pop
    # filter non-valid OS-es
250 1f9430d6 Iustin Pop
    os_set = [val[0] for val in result if val[1]]
251 175f44c2 Iustin Pop
252 175f44c2 Iustin Pop
    if self.opts.os not in os_set:
253 21546b1c Iustin Pop
      Log("OS '%s' not found" % self.opts.os)
254 175f44c2 Iustin Pop
      sys.exit(1)
255 175f44c2 Iustin Pop
256 175f44c2 Iustin Pop
  def CreateInstances(self):
257 175f44c2 Iustin Pop
    """Create the given instances.
258 175f44c2 Iustin Pop
259 175f44c2 Iustin Pop
    """
260 175f44c2 Iustin Pop
    self.to_rem = []
261 175f44c2 Iustin Pop
    mytor = izip(cycle(self.nodes),
262 175f44c2 Iustin Pop
                 islice(cycle(self.nodes), 1, None),
263 175f44c2 Iustin Pop
                 self.instances)
264 ec5c88dc Iustin Pop
    jobset = []
265 338e51e8 Iustin Pop
266 175f44c2 Iustin Pop
    for pnode, snode, instance in mytor:
267 b91bde14 Iustin Pop
      if self.opts.iallocator:
268 b91bde14 Iustin Pop
        pnode = snode = None
269 6d54548e Guido Trotter
        Log("- Add instance %s (iallocator: %s)" %
270 6d54548e Guido Trotter
              (instance, self.opts.iallocator))
271 6d54548e Guido Trotter
      elif self.opts.disk_template not in constants.DTS_NET_MIRROR:
272 6d54548e Guido Trotter
        snode = None
273 6d54548e Guido Trotter
        Log("- Add instance %s on node %s" % (instance, pnode))
274 6d54548e Guido Trotter
      else:
275 6d54548e Guido Trotter
        Log("- Add instance %s on nodes %s/%s" % (instance, pnode, snode))
276 6d54548e Guido Trotter
277 175f44c2 Iustin Pop
      op = opcodes.OpCreateInstance(instance_name=instance,
278 08db7c5c Iustin Pop
                                    disks = [ {"size": size}
279 08db7c5c Iustin Pop
                                              for size in self.disk_size],
280 175f44c2 Iustin Pop
                                    disk_template=self.opts.disk_template,
281 b518a14a Iustin Pop
                                    nics=self.opts.nics,
282 a8083063 Iustin Pop
                                    mode=constants.INSTANCE_CREATE,
283 175f44c2 Iustin Pop
                                    os_type=self.opts.os,
284 175f44c2 Iustin Pop
                                    pnode=pnode,
285 175f44c2 Iustin Pop
                                    snode=snode,
286 a8083063 Iustin Pop
                                    start=True,
287 e9f745aa Iustin Pop
                                    ip_check=True,
288 4501af56 Iustin Pop
                                    wait_for_sync=True,
289 2f505cb5 Manuel Franceschini
                                    file_driver="loop",
290 b91bde14 Iustin Pop
                                    file_storage_dir=None,
291 6e2dc934 Alexander Schreiber
                                    iallocator=self.opts.iallocator,
292 338e51e8 Iustin Pop
                                    beparams=self.bep,
293 338e51e8 Iustin Pop
                                    hvparams=self.hvp,
294 338e51e8 Iustin Pop
                                    )
295 6e2dc934 Alexander Schreiber
296 ec5c88dc Iustin Pop
      if self.opts.parallel:
297 ec5c88dc Iustin Pop
        jobset.append([op])
298 ec5c88dc Iustin Pop
        # FIXME: here we should not append to to_rem uncoditionally,
299 ec5c88dc Iustin Pop
        # but only when the job is successful
300 ec5c88dc Iustin Pop
        self.to_rem.append(instance)
301 ec5c88dc Iustin Pop
      else:
302 ec5c88dc Iustin Pop
        self.ExecOp(op)
303 ec5c88dc Iustin Pop
        self.to_rem.append(instance)
304 ec5c88dc Iustin Pop
    if self.opts.parallel:
305 ec5c88dc Iustin Pop
      self.ExecJobSet(jobset)
306 175f44c2 Iustin Pop
307 659712c8 Iustin Pop
  def GrowDisks(self):
308 659712c8 Iustin Pop
    """Grow both the os and the swap disks by the requested amount, if any."""
309 659712c8 Iustin Pop
    for instance in self.instances:
310 08db7c5c Iustin Pop
      for idx, growth in enumerate(self.disk_growth):
311 659712c8 Iustin Pop
        if growth > 0:
312 08db7c5c Iustin Pop
          op = opcodes.OpGrowDisk(instance_name=instance, disk=idx,
313 6605411d Iustin Pop
                                  amount=growth, wait_for_sync=True)
314 084a9f22 Iustin Pop
          Log("- Increase %s's disk/%s by %s MB" % (instance, idx, growth))
315 659712c8 Iustin Pop
          self.ExecOp(op)
316 659712c8 Iustin Pop
317 175f44c2 Iustin Pop
  def ReplaceDisks1D8(self):
318 175f44c2 Iustin Pop
    """Replace disks on primary and secondary for drbd8."""
319 175f44c2 Iustin Pop
    for instance in self.instances:
320 175f44c2 Iustin Pop
      for mode in constants.REPLACE_DISK_SEC, constants.REPLACE_DISK_PRI:
321 175f44c2 Iustin Pop
        op = opcodes.OpReplaceDisks(instance_name=instance,
322 175f44c2 Iustin Pop
                                    mode=mode,
323 08db7c5c Iustin Pop
                                    disks=[i for i in range(self.disk_count)])
324 21546b1c Iustin Pop
        Log("- Replace disks (%s) for instance %s" % (mode, instance))
325 21546b1c Iustin Pop
        self.ExecOp(op)
326 175f44c2 Iustin Pop
327 175f44c2 Iustin Pop
  def ReplaceDisks2(self):
328 175f44c2 Iustin Pop
    """Replace secondary node."""
329 f9193417 Iustin Pop
    mode = constants.REPLACE_DISK_SEC
330 175f44c2 Iustin Pop
331 175f44c2 Iustin Pop
    mytor = izip(islice(cycle(self.nodes), 2, None),
332 175f44c2 Iustin Pop
                 self.instances)
333 175f44c2 Iustin Pop
    for tnode, instance in mytor:
334 b6e82a65 Iustin Pop
      if self.opts.iallocator:
335 b6e82a65 Iustin Pop
        tnode = None
336 175f44c2 Iustin Pop
      op = opcodes.OpReplaceDisks(instance_name=instance,
337 175f44c2 Iustin Pop
                                  mode=mode,
338 175f44c2 Iustin Pop
                                  remote_node=tnode,
339 b6e82a65 Iustin Pop
                                  iallocator=self.opts.iallocator,
340 08db7c5c Iustin Pop
                                  disks=[i for i in range(self.disk_count)])
341 21546b1c Iustin Pop
      Log("- Replace secondary (%s) for instance %s" % (mode, instance))
342 21546b1c Iustin Pop
      self.ExecOp(op)
343 175f44c2 Iustin Pop
344 175f44c2 Iustin Pop
  def Failover(self):
345 175f44c2 Iustin Pop
    """Failover the instances."""
346 175f44c2 Iustin Pop
347 175f44c2 Iustin Pop
    for instance in self.instances:
348 175f44c2 Iustin Pop
      op = opcodes.OpFailoverInstance(instance_name=instance,
349 175f44c2 Iustin Pop
                                      ignore_consistency=False)
350 175f44c2 Iustin Pop
351 21546b1c Iustin Pop
      Log("- Failover instance %s" % (instance))
352 21546b1c Iustin Pop
      self.ExecOp(op)
353 175f44c2 Iustin Pop
354 bd5e77f9 Guido Trotter
  def ImportExport(self):
355 bd5e77f9 Guido Trotter
    """Export the instance, delete it, and import it back.
356 bd5e77f9 Guido Trotter
357 bd5e77f9 Guido Trotter
    """
358 bd5e77f9 Guido Trotter
359 bd5e77f9 Guido Trotter
    mytor = izip(cycle(self.nodes),
360 bd5e77f9 Guido Trotter
                 islice(cycle(self.nodes), 1, None),
361 bd5e77f9 Guido Trotter
                 islice(cycle(self.nodes), 2, None),
362 bd5e77f9 Guido Trotter
                 self.instances)
363 bd5e77f9 Guido Trotter
364 bd5e77f9 Guido Trotter
    for pnode, snode, enode, instance in mytor:
365 f9af35c8 Guido Trotter
366 f9af35c8 Guido Trotter
      if self.opts.iallocator:
367 f9af35c8 Guido Trotter
        pnode = snode = None
368 00f91f29 Iustin Pop
        import_log_msg = ("- Import instance %s from node %s"
369 00f91f29 Iustin Pop
                          " (iallocator: %s)" %
370 f9af35c8 Guido Trotter
                          (instance, enode, self.opts.iallocator))
371 f9af35c8 Guido Trotter
      elif self.opts.disk_template not in constants.DTS_NET_MIRROR:
372 f9af35c8 Guido Trotter
        snode = None
373 f9af35c8 Guido Trotter
        import_log_msg = ("- Import instance %s from node %s to node %s" %
374 f9af35c8 Guido Trotter
                          (instance, enode, pnode))
375 f9af35c8 Guido Trotter
      else:
376 f9af35c8 Guido Trotter
        import_log_msg = ("- Import instance %s from node %s to nodes %s/%s" %
377 f9af35c8 Guido Trotter
                          (instance, enode, pnode, snode))
378 f9af35c8 Guido Trotter
379 bd5e77f9 Guido Trotter
      exp_op = opcodes.OpExportInstance(instance_name=instance,
380 bd5e77f9 Guido Trotter
                                           target_node=enode,
381 bd5e77f9 Guido Trotter
                                           shutdown=True)
382 5c54b832 Iustin Pop
      rem_op = opcodes.OpRemoveInstance(instance_name=instance,
383 5c54b832 Iustin Pop
                                        ignore_failures=True)
384 bd5e77f9 Guido Trotter
      nam_op = opcodes.OpQueryInstances(output_fields=["name"],
385 bd5e77f9 Guido Trotter
                                           names=[instance])
386 bd5e77f9 Guido Trotter
      full_name = self.ExecOp(nam_op)[0][0]
387 bd5e77f9 Guido Trotter
      imp_dir = os.path.join(constants.EXPORT_DIR, full_name)
388 bd5e77f9 Guido Trotter
      imp_op = opcodes.OpCreateInstance(instance_name=instance,
389 0ca35d45 Guido Trotter
                                        disks = [ {"size": size}
390 0ca35d45 Guido Trotter
                                                  for size in self.disk_size],
391 bd5e77f9 Guido Trotter
                                        disk_template=self.opts.disk_template,
392 b518a14a Iustin Pop
                                        nics=self.opts.nics,
393 bd5e77f9 Guido Trotter
                                        mode=constants.INSTANCE_IMPORT,
394 bd5e77f9 Guido Trotter
                                        src_node=enode,
395 bd5e77f9 Guido Trotter
                                        src_path=imp_dir,
396 bd5e77f9 Guido Trotter
                                        pnode=pnode,
397 bd5e77f9 Guido Trotter
                                        snode=snode,
398 bd5e77f9 Guido Trotter
                                        start=True,
399 bd5e77f9 Guido Trotter
                                        ip_check=True,
400 bd5e77f9 Guido Trotter
                                        wait_for_sync=True,
401 96bb2f71 Manuel Franceschini
                                        file_storage_dir=None,
402 0ca35d45 Guido Trotter
                                        file_driver="loop",
403 6e2dc934 Alexander Schreiber
                                        iallocator=self.opts.iallocator,
404 338e51e8 Iustin Pop
                                        beparams=self.bep,
405 338e51e8 Iustin Pop
                                        hvparams=self.hvp,
406 338e51e8 Iustin Pop
                                        )
407 6e2dc934 Alexander Schreiber
408 4a7ff493 Guido Trotter
      erem_op = opcodes.OpRemoveExport(instance_name=instance)
409 bd5e77f9 Guido Trotter
410 bd5e77f9 Guido Trotter
      Log("- Export instance %s to node %s" % (instance, enode))
411 bd5e77f9 Guido Trotter
      self.ExecOp(exp_op)
412 bd5e77f9 Guido Trotter
      Log("- Remove instance %s" % (instance))
413 bd5e77f9 Guido Trotter
      self.ExecOp(rem_op)
414 bd5e77f9 Guido Trotter
      self.to_rem.remove(instance)
415 f9af35c8 Guido Trotter
      Log(import_log_msg)
416 bd5e77f9 Guido Trotter
      self.ExecOp(imp_op)
417 4a7ff493 Guido Trotter
      Log("- Remove export of instance %s" % (instance))
418 4a7ff493 Guido Trotter
      self.ExecOp(erem_op)
419 4a7ff493 Guido Trotter
420 bd5e77f9 Guido Trotter
      self.to_rem.append(instance)
421 bd5e77f9 Guido Trotter
422 054a8696 Manuel Franceschini
  def StopInstance(self, instance):
423 054a8696 Manuel Franceschini
    """Stop given instance."""
424 054a8696 Manuel Franceschini
    op = opcodes.OpShutdownInstance(instance_name=instance)
425 054a8696 Manuel Franceschini
    Log("- Shutdown instance %s" % instance)
426 054a8696 Manuel Franceschini
    self.ExecOp(op)
427 054a8696 Manuel Franceschini
428 054a8696 Manuel Franceschini
  def StartInstance(self, instance):
429 054a8696 Manuel Franceschini
    """Start given instance."""
430 054a8696 Manuel Franceschini
    op = opcodes.OpStartupInstance(instance_name=instance, force=False)
431 054a8696 Manuel Franceschini
    Log("- Start instance %s" % instance)
432 054a8696 Manuel Franceschini
    self.ExecOp(op)
433 054a8696 Manuel Franceschini
434 054a8696 Manuel Franceschini
  def RenameInstance(self, instance, instance_new):
435 054a8696 Manuel Franceschini
    """Rename instance."""
436 054a8696 Manuel Franceschini
    op = opcodes.OpRenameInstance(instance_name=instance,
437 054a8696 Manuel Franceschini
                                  new_name=instance_new)
438 054a8696 Manuel Franceschini
    Log("- Rename instance %s to %s" % (instance, instance_new))
439 054a8696 Manuel Franceschini
    self.ExecOp(op)
440 054a8696 Manuel Franceschini
441 175f44c2 Iustin Pop
  def StopStart(self):
442 175f44c2 Iustin Pop
    """Stop/start the instances."""
443 175f44c2 Iustin Pop
    for instance in self.instances:
444 054a8696 Manuel Franceschini
      self.StopInstance(instance)
445 054a8696 Manuel Franceschini
      self.StartInstance(instance)
446 175f44c2 Iustin Pop
447 175f44c2 Iustin Pop
  def Remove(self):
448 175f44c2 Iustin Pop
    """Remove the instances."""
449 175f44c2 Iustin Pop
    for instance in self.to_rem:
450 5c54b832 Iustin Pop
      op = opcodes.OpRemoveInstance(instance_name=instance,
451 5c54b832 Iustin Pop
                                    ignore_failures=True)
452 21546b1c Iustin Pop
      Log("- Remove instance %s" % instance)
453 21546b1c Iustin Pop
      self.ExecOp(op)
454 175f44c2 Iustin Pop
455 054a8696 Manuel Franceschini
456 054a8696 Manuel Franceschini
  def Rename(self):
457 054a8696 Manuel Franceschini
    """Rename the instances."""
458 054a8696 Manuel Franceschini
    rename = self.opts.rename
459 054a8696 Manuel Franceschini
    for instance in self.instances:
460 054a8696 Manuel Franceschini
      self.StopInstance(instance)
461 054a8696 Manuel Franceschini
      self.RenameInstance(instance, rename)
462 054a8696 Manuel Franceschini
      self.StartInstance(rename)
463 054a8696 Manuel Franceschini
      self.StopInstance(rename)
464 054a8696 Manuel Franceschini
      self.RenameInstance(rename, instance)
465 054a8696 Manuel Franceschini
      self.StartInstance(instance)
466 054a8696 Manuel Franceschini
467 00f91f29 Iustin Pop
  def Reinstall(self):
468 00f91f29 Iustin Pop
    """Reinstall the instances."""
469 00f91f29 Iustin Pop
    for instance in self.instances:
470 00f91f29 Iustin Pop
      self.StopInstance(instance)
471 00f91f29 Iustin Pop
      op = opcodes.OpReinstallInstance(instance_name=instance)
472 00f91f29 Iustin Pop
      Log("- Reinstall instance %s without passing the OS" % (instance,))
473 00f91f29 Iustin Pop
      self.ExecOp(op)
474 00f91f29 Iustin Pop
      op = opcodes.OpReinstallInstance(instance_name=instance,
475 00f91f29 Iustin Pop
                                       os_type=self.opts.os)
476 00f91f29 Iustin Pop
      Log("- Reinstall instance %s specifying the OS" % (instance,))
477 00f91f29 Iustin Pop
      self.ExecOp(op)
478 00f91f29 Iustin Pop
      self.StartInstance(instance)
479 00f91f29 Iustin Pop
480 00f91f29 Iustin Pop
  def Reboot(self):
481 00f91f29 Iustin Pop
    """Reinstall the instances."""
482 00f91f29 Iustin Pop
    for instance in self.instances:
483 00f91f29 Iustin Pop
      for reboot_type in constants.REBOOT_TYPES:
484 00f91f29 Iustin Pop
        op = opcodes.OpRebootInstance(instance_name=instance,
485 00f91f29 Iustin Pop
                                      reboot_type=reboot_type,
486 00f91f29 Iustin Pop
                                      ignore_secondaries=False)
487 00f91f29 Iustin Pop
        Log("- Reboot instance %s with type '%s'" % (instance, reboot_type))
488 00f91f29 Iustin Pop
        self.ExecOp(op)
489 00f91f29 Iustin Pop
490 175f44c2 Iustin Pop
  def BurninCluster(self):
491 175f44c2 Iustin Pop
    """Test a cluster intensively.
492 175f44c2 Iustin Pop
493 175f44c2 Iustin Pop
    This will create instances and then start/stop/failover them.
494 175f44c2 Iustin Pop
    It is safe for existing instances but could impact performance.
495 175f44c2 Iustin Pop
496 175f44c2 Iustin Pop
    """
497 175f44c2 Iustin Pop
498 175f44c2 Iustin Pop
    opts = self.opts
499 175f44c2 Iustin Pop
500 21546b1c Iustin Pop
    Log("- Testing global parameters")
501 175f44c2 Iustin Pop
502 bd249e2f Iustin Pop
    if (len(self.nodes) == 1 and
503 2f505cb5 Manuel Franceschini
        opts.disk_template not in (constants.DT_DISKLESS, constants.DT_PLAIN,
504 2f505cb5 Manuel Franceschini
                                   constants.DT_FILE)):
505 21546b1c Iustin Pop
      Log("When one node is available/selected the disk template must"
506 2f505cb5 Manuel Franceschini
          " be 'diskless', 'file' or 'plain'")
507 175f44c2 Iustin Pop
      sys.exit(1)
508 175f44c2 Iustin Pop
509 21546b1c Iustin Pop
    has_err = True
510 175f44c2 Iustin Pop
    try:
511 175f44c2 Iustin Pop
      self.CreateInstances()
512 175f44c2 Iustin Pop
      if opts.do_replace1 and opts.disk_template in constants.DTS_NET_MIRROR:
513 f9193417 Iustin Pop
        self.ReplaceDisks1D8()
514 175f44c2 Iustin Pop
      if (opts.do_replace2 and len(self.nodes) > 2 and
515 175f44c2 Iustin Pop
          opts.disk_template in constants.DTS_NET_MIRROR) :
516 175f44c2 Iustin Pop
        self.ReplaceDisks2()
517 175f44c2 Iustin Pop
518 659712c8 Iustin Pop
      if opts.disk_template != constants.DT_DISKLESS:
519 659712c8 Iustin Pop
        self.GrowDisks()
520 659712c8 Iustin Pop
521 175f44c2 Iustin Pop
      if opts.do_failover and opts.disk_template in constants.DTS_NET_MIRROR:
522 175f44c2 Iustin Pop
        self.Failover()
523 175f44c2 Iustin Pop
524 a5e7be6b Iustin Pop
      if (opts.do_importexport and
525 a5e7be6b Iustin Pop
          opts.disk_template not in (constants.DT_DISKLESS,
526 a5e7be6b Iustin Pop
                                     constants.DT_FILE)):
527 bd5e77f9 Guido Trotter
        self.ImportExport()
528 bd5e77f9 Guido Trotter
529 00f91f29 Iustin Pop
      if opts.do_reinstall:
530 00f91f29 Iustin Pop
        self.Reinstall()
531 00f91f29 Iustin Pop
532 00f91f29 Iustin Pop
      if opts.do_reboot:
533 00f91f29 Iustin Pop
        self.Reboot()
534 00f91f29 Iustin Pop
535 d4844f0f Guido Trotter
      if opts.do_startstop:
536 d4844f0f Guido Trotter
        self.StopStart()
537 d4844f0f Guido Trotter
538 054a8696 Manuel Franceschini
      if opts.rename:
539 054a8696 Manuel Franceschini
        self.Rename()
540 054a8696 Manuel Franceschini
541 21546b1c Iustin Pop
      has_err = False
542 175f44c2 Iustin Pop
    finally:
543 21546b1c Iustin Pop
      if has_err:
544 21546b1c Iustin Pop
        Log("Error detected: opcode buffer follows:\n\n")
545 21546b1c Iustin Pop
        Log(self.GetFeedbackBuf())
546 21546b1c Iustin Pop
        Log("\n\n")
547 175f44c2 Iustin Pop
      self.Remove()
548 175f44c2 Iustin Pop
549 175f44c2 Iustin Pop
    return 0
550 a8083063 Iustin Pop
551 01b69ec5 Michael Hanselmann
552 a8083063 Iustin Pop
def main():
553 3ecf6786 Iustin Pop
  """Main function"""
554 3ecf6786 Iustin Pop
555 175f44c2 Iustin Pop
  burner = Burner()
556 a4af651e Iustin Pop
  return burner.BurninCluster()
557 a8083063 Iustin Pop
558 01b69ec5 Michael Hanselmann
559 a8083063 Iustin Pop
if __name__ == "__main__":
560 3ecf6786 Iustin Pop
  main()