Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 936f3c59

History | View | Annotate | Download (32.4 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 5178f1bc Iustin Pop
import socket
31 5dc626fd Iustin Pop
import urllib
32 175f44c2 Iustin Pop
from itertools import izip, islice, cycle
33 21546b1c Iustin Pop
from cStringIO import StringIO
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
from ganeti import opcodes
36 a8083063 Iustin Pop
from ganeti import constants
37 a8083063 Iustin Pop
from ganeti import cli
38 9f13fc7a Iustin Pop
from ganeti import errors
39 9f13fc7a Iustin Pop
from ganeti import utils
40 a8083063 Iustin Pop
41 01b69ec5 Michael Hanselmann
42 9f13fc7a Iustin Pop
USAGE = ("\tburnin -o OS_NAME [options...] instance_name ...")
43 a8083063 Iustin Pop
44 73ff3118 Iustin Pop
MAX_RETRIES = 3
45 01b69ec5 Michael Hanselmann
46 5178f1bc Iustin Pop
class InstanceDown(Exception):
47 5178f1bc Iustin Pop
  """The checked instance was not up"""
48 5178f1bc Iustin Pop
49 5178f1bc Iustin Pop
50 73ff3118 Iustin Pop
class BurninFailure(Exception):
51 73ff3118 Iustin Pop
  """Failure detected during burning"""
52 73ff3118 Iustin Pop
53 73ff3118 Iustin Pop
54 a8083063 Iustin Pop
def Usage():
55 a8083063 Iustin Pop
  """Shows program usage information and exits the program."""
56 a8083063 Iustin Pop
57 a8083063 Iustin Pop
  print >> sys.stderr, "Usage:"
58 a8083063 Iustin Pop
  print >> sys.stderr, USAGE
59 a8083063 Iustin Pop
  sys.exit(2)
60 a8083063 Iustin Pop
61 01b69ec5 Michael Hanselmann
62 836d59d7 Iustin Pop
def Log(msg, indent=0):
63 3ecf6786 Iustin Pop
  """Simple function that prints out its argument.
64 3ecf6786 Iustin Pop
65 3ecf6786 Iustin Pop
  """
66 836d59d7 Iustin Pop
  headers = {
67 836d59d7 Iustin Pop
    0: "- ",
68 836d59d7 Iustin Pop
    1: "* ",
69 836d59d7 Iustin Pop
    2: ""
70 836d59d7 Iustin Pop
    }
71 836d59d7 Iustin Pop
  sys.stdout.write("%*s%s%s\n" % (2*indent, "",
72 836d59d7 Iustin Pop
                                   headers.get(indent, "  "), msg))
73 84cc52ab Michael Hanselmann
  sys.stdout.flush()
74 a8083063 Iustin Pop
75 836d59d7 Iustin Pop
def Err(msg, exit_code=1):
76 836d59d7 Iustin Pop
  """Simple error logging that prints to stderr.
77 836d59d7 Iustin Pop
78 836d59d7 Iustin Pop
  """
79 836d59d7 Iustin Pop
  sys.stderr.write(msg + "\n")
80 836d59d7 Iustin Pop
  sys.stderr.flush()
81 836d59d7 Iustin Pop
  sys.exit(exit_code)
82 01b69ec5 Michael Hanselmann
83 5dc626fd Iustin Pop
84 5dc626fd Iustin Pop
class SimpleOpener(urllib.FancyURLopener):
85 5dc626fd Iustin Pop
  """A simple url opener"""
86 5dc626fd Iustin Pop
87 5dc626fd Iustin Pop
  def prompt_user_passwd(self, host, realm, clear_cache = 0):
88 5dc626fd Iustin Pop
    """No-interaction version of prompt_user_passwd."""
89 5dc626fd Iustin Pop
    return None, None
90 5dc626fd Iustin Pop
91 5dc626fd Iustin Pop
  def http_error_default(self, url, fp, errcode, errmsg, headers):
92 5dc626fd Iustin Pop
    """Custom error handling"""
93 5dc626fd Iustin Pop
    # make sure sockets are not left in CLOSE_WAIT, this is similar
94 5dc626fd Iustin Pop
    # but with a different exception to the BasicURLOpener class
95 5dc626fd Iustin Pop
    _ = fp.read() # throw away data
96 5dc626fd Iustin Pop
    fp.close()
97 5dc626fd Iustin Pop
    raise InstanceDown("HTTP error returned: code %s, msg %s" %
98 5dc626fd Iustin Pop
                       (errcode, errmsg))
99 5dc626fd Iustin Pop
100 5dc626fd Iustin Pop
101 10eb54fb Michael Hanselmann
OPTIONS = [
102 10eb54fb Michael Hanselmann
  cli.cli_option("-o", "--os", dest="os", default=None,
103 10eb54fb Michael Hanselmann
                 help="OS to use during burnin",
104 a52ba89d Michael Hanselmann
                 metavar="<OS>",
105 a52ba89d Michael Hanselmann
                 completion_suggest=cli.OPT_COMPL_ONE_OS),
106 10eb54fb Michael Hanselmann
  cli.cli_option("--disk-size", dest="disk_size",
107 10eb54fb Michael Hanselmann
                 help="Disk size (determines disk count)",
108 10eb54fb Michael Hanselmann
                 default="128m", type="string", metavar="<size,size,...>",
109 10eb54fb Michael Hanselmann
                 completion_suggest=("128M 512M 1G 4G 1G,256M"
110 10eb54fb Michael Hanselmann
                                     " 4G,1G,1G 10G").split()),
111 10eb54fb Michael Hanselmann
  cli.cli_option("--disk-growth", dest="disk_growth", help="Disk growth",
112 10eb54fb Michael Hanselmann
                 default="128m", type="string", metavar="<size,size,...>"),
113 10eb54fb Michael Hanselmann
  cli.cli_option("--mem-size", dest="mem_size", help="Memory size",
114 10eb54fb Michael Hanselmann
                 default=128, type="unit", metavar="<size>",
115 10eb54fb Michael Hanselmann
                 completion_suggest=("128M 256M 512M 1G 4G 8G"
116 10eb54fb Michael Hanselmann
                                     " 12G 16G").split()),
117 9cdb9578 Iustin Pop
  cli.VERBOSE_OPT,
118 10eb54fb Michael Hanselmann
  cli.cli_option("--no-replace1", dest="do_replace1",
119 10eb54fb Michael Hanselmann
                 help="Skip disk replacement with the same secondary",
120 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
121 10eb54fb Michael Hanselmann
  cli.cli_option("--no-replace2", dest="do_replace2",
122 10eb54fb Michael Hanselmann
                 help="Skip disk replacement with a different secondary",
123 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
124 10eb54fb Michael Hanselmann
  cli.cli_option("--no-failover", dest="do_failover",
125 10eb54fb Michael Hanselmann
                 help="Skip instance failovers", action="store_false",
126 10eb54fb Michael Hanselmann
                 default=True),
127 10eb54fb Michael Hanselmann
  cli.cli_option("--no-migrate", dest="do_migrate",
128 10eb54fb Michael Hanselmann
                 help="Skip instance live migration",
129 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
130 5b9107ff Iustin Pop
  cli.cli_option("--no-move", dest="do_move",
131 5b9107ff Iustin Pop
                 help="Skip instance moves", action="store_false",
132 5b9107ff Iustin Pop
                 default=True),
133 10eb54fb Michael Hanselmann
  cli.cli_option("--no-importexport", dest="do_importexport",
134 10eb54fb Michael Hanselmann
                 help="Skip instance export/import", action="store_false",
135 10eb54fb Michael Hanselmann
                 default=True),
136 10eb54fb Michael Hanselmann
  cli.cli_option("--no-startstop", dest="do_startstop",
137 10eb54fb Michael Hanselmann
                 help="Skip instance stop/start", action="store_false",
138 10eb54fb Michael Hanselmann
                 default=True),
139 10eb54fb Michael Hanselmann
  cli.cli_option("--no-reinstall", dest="do_reinstall",
140 10eb54fb Michael Hanselmann
                 help="Skip instance reinstall", action="store_false",
141 10eb54fb Michael Hanselmann
                 default=True),
142 10eb54fb Michael Hanselmann
  cli.cli_option("--no-reboot", dest="do_reboot",
143 10eb54fb Michael Hanselmann
                 help="Skip instance reboot", action="store_false",
144 10eb54fb Michael Hanselmann
                 default=True),
145 10eb54fb Michael Hanselmann
  cli.cli_option("--no-activate-disks", dest="do_activate_disks",
146 10eb54fb Michael Hanselmann
                 help="Skip disk activation/deactivation",
147 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
148 10eb54fb Michael Hanselmann
  cli.cli_option("--no-add-disks", dest="do_addremove_disks",
149 10eb54fb Michael Hanselmann
                 help="Skip disk addition/removal",
150 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
151 10eb54fb Michael Hanselmann
  cli.cli_option("--no-add-nics", dest="do_addremove_nics",
152 10eb54fb Michael Hanselmann
                 help="Skip NIC addition/removal",
153 10eb54fb Michael Hanselmann
                 action="store_false", default=True),
154 10eb54fb Michael Hanselmann
  cli.cli_option("--no-nics", dest="nics",
155 10eb54fb Michael Hanselmann
                 help="No network interfaces", action="store_const",
156 10eb54fb Michael Hanselmann
                 const=[], default=[{}]),
157 10eb54fb Michael Hanselmann
  cli.cli_option("--rename", dest="rename", default=None,
158 10eb54fb Michael Hanselmann
                 help=("Give one unused instance name which is taken"
159 10eb54fb Michael Hanselmann
                       " to start the renaming sequence"),
160 10eb54fb Michael Hanselmann
                 metavar="<instance_name>"),
161 10eb54fb Michael Hanselmann
  cli.cli_option("-t", "--disk-template", dest="disk_template",
162 10eb54fb Michael Hanselmann
                 choices=list(constants.DISK_TEMPLATES),
163 10eb54fb Michael Hanselmann
                 default=constants.DT_DRBD8,
164 10eb54fb Michael Hanselmann
                 help="Disk template (diskless, file, plain or drbd) [drbd]"),
165 10eb54fb Michael Hanselmann
  cli.cli_option("-n", "--nodes", dest="nodes", default="",
166 10eb54fb Michael Hanselmann
                 help=("Comma separated list of nodes to perform"
167 a52ba89d Michael Hanselmann
                       " the burnin on (defaults to all nodes)"),
168 a52ba89d Michael Hanselmann
                 completion_suggest=cli.OPT_COMPL_MANY_NODES),
169 10eb54fb Michael Hanselmann
  cli.cli_option("-I", "--iallocator", dest="iallocator",
170 10eb54fb Michael Hanselmann
                 default=None, type="string",
171 10eb54fb Michael Hanselmann
                 help=("Perform the allocation using an iallocator"
172 10eb54fb Michael Hanselmann
                       " instead of fixed node spread (node restrictions no"
173 10eb54fb Michael Hanselmann
                       " longer apply, therefore -n/--nodes must not be"
174 a52ba89d Michael Hanselmann
                       " used"),
175 a52ba89d Michael Hanselmann
                 completion_suggest=cli.OPT_COMPL_ONE_IALLOCATOR),
176 10eb54fb Michael Hanselmann
  cli.cli_option("-p", "--parallel", default=False, action="store_true",
177 10eb54fb Michael Hanselmann
                 dest="parallel",
178 10eb54fb Michael Hanselmann
                 help=("Enable parallelization of some operations in"
179 10eb54fb Michael Hanselmann
                       " order to speed burnin or to test granular locking")),
180 10eb54fb Michael Hanselmann
  cli.cli_option("--net-timeout", default=15, type="int",
181 10eb54fb Michael Hanselmann
                 dest="net_timeout",
182 10eb54fb Michael Hanselmann
                 help=("The instance check network timeout in seconds"
183 10eb54fb Michael Hanselmann
                       " (defaults to 15 seconds)"),
184 10eb54fb Michael Hanselmann
                 completion_suggest="15 60 300 900".split()),
185 10eb54fb Michael Hanselmann
  cli.cli_option("-C", "--http-check", default=False, action="store_true",
186 10eb54fb Michael Hanselmann
                 dest="http_check",
187 10eb54fb Michael Hanselmann
                 help=("Enable checking of instance status via http,"
188 10eb54fb Michael Hanselmann
                       " looking for /hostname.txt that should contain the"
189 10eb54fb Michael Hanselmann
                       " name of the instance")),
190 10eb54fb Michael Hanselmann
  cli.cli_option("-K", "--keep-instances", default=False,
191 10eb54fb Michael Hanselmann
                 action="store_true",
192 10eb54fb Michael Hanselmann
                 dest="keep_instances",
193 10eb54fb Michael Hanselmann
                 help=("Leave instances on the cluster after burnin,"
194 10eb54fb Michael Hanselmann
                       " for investigation in case of errors or simply"
195 10eb54fb Michael Hanselmann
                       " to use them")),
196 10eb54fb Michael Hanselmann
  ]
197 10eb54fb Michael Hanselmann
198 10eb54fb Michael Hanselmann
# Mainly used for bash completion
199 10eb54fb Michael Hanselmann
ARGUMENTS = [cli.ArgInstance(min=1)]
200 10eb54fb Michael Hanselmann
201 10eb54fb Michael Hanselmann
202 175f44c2 Iustin Pop
class Burner(object):
203 175f44c2 Iustin Pop
  """Burner class."""
204 175f44c2 Iustin Pop
205 175f44c2 Iustin Pop
  def __init__(self):
206 175f44c2 Iustin Pop
    """Constructor."""
207 82d9caef Iustin Pop
    utils.SetupLogging(constants.LOG_BURNIN, debug=False, stderr_logging=True)
208 5dc626fd Iustin Pop
    self.url_opener = SimpleOpener()
209 21546b1c Iustin Pop
    self._feed_buf = StringIO()
210 175f44c2 Iustin Pop
    self.nodes = []
211 175f44c2 Iustin Pop
    self.instances = []
212 175f44c2 Iustin Pop
    self.to_rem = []
213 c723c163 Iustin Pop
    self.queued_ops = []
214 175f44c2 Iustin Pop
    self.opts = None
215 73ff3118 Iustin Pop
    self.queue_retry = False
216 73ff3118 Iustin Pop
    self.disk_count = self.disk_growth = self.disk_size = None
217 73ff3118 Iustin Pop
    self.hvp = self.bep = None
218 175f44c2 Iustin Pop
    self.ParseOptions()
219 320eda24 Iustin Pop
    self.cl = cli.GetClient()
220 175f44c2 Iustin Pop
    self.GetState()
221 175f44c2 Iustin Pop
222 21546b1c Iustin Pop
  def ClearFeedbackBuf(self):
223 21546b1c Iustin Pop
    """Clear the feedback buffer."""
224 21546b1c Iustin Pop
    self._feed_buf.truncate(0)
225 21546b1c Iustin Pop
226 21546b1c Iustin Pop
  def GetFeedbackBuf(self):
227 21546b1c Iustin Pop
    """Return the contents of the buffer."""
228 21546b1c Iustin Pop
    return self._feed_buf.getvalue()
229 21546b1c Iustin Pop
230 21546b1c Iustin Pop
  def Feedback(self, msg):
231 21546b1c Iustin Pop
    """Acumulate feedback in our buffer."""
232 88d31e5c Iustin Pop
    formatted_msg = "%s %s" % (time.ctime(utils.MergeTime(msg[0])), msg[2])
233 88d31e5c Iustin Pop
    self._feed_buf.write(formatted_msg + "\n")
234 d7b47a77 Iustin Pop
    if self.opts.verbose:
235 88d31e5c Iustin Pop
      Log(formatted_msg, indent=3)
236 21546b1c Iustin Pop
237 73ff3118 Iustin Pop
  def MaybeRetry(self, retry_count, msg, fn, *args):
238 73ff3118 Iustin Pop
    """Possibly retry a given function execution.
239 73ff3118 Iustin Pop
240 73ff3118 Iustin Pop
    @type retry_count: int
241 73ff3118 Iustin Pop
    @param retry_count: retry counter:
242 73ff3118 Iustin Pop
        - 0: non-retryable action
243 73ff3118 Iustin Pop
        - 1: last retry for a retryable action
244 73ff3118 Iustin Pop
        - MAX_RETRIES: original try for a retryable action
245 73ff3118 Iustin Pop
    @type msg: str
246 73ff3118 Iustin Pop
    @param msg: the kind of the operation
247 73ff3118 Iustin Pop
    @type fn: callable
248 73ff3118 Iustin Pop
    @param fn: the function to be called
249 73ff3118 Iustin Pop
250 73ff3118 Iustin Pop
    """
251 73ff3118 Iustin Pop
    try:
252 73ff3118 Iustin Pop
      val = fn(*args)
253 73ff3118 Iustin Pop
      if retry_count > 0 and retry_count < MAX_RETRIES:
254 73ff3118 Iustin Pop
        Log("Idempotent %s succeeded after %d retries" %
255 73ff3118 Iustin Pop
            (msg, MAX_RETRIES - retry_count))
256 73ff3118 Iustin Pop
      return val
257 73ff3118 Iustin Pop
    except Exception, err:
258 73ff3118 Iustin Pop
      if retry_count == 0:
259 73ff3118 Iustin Pop
        Log("Non-idempotent %s failed, aborting" % (msg, ))
260 73ff3118 Iustin Pop
        raise
261 73ff3118 Iustin Pop
      elif retry_count == 1:
262 73ff3118 Iustin Pop
        Log("Idempotent %s repeated failure, aborting" % (msg, ))
263 73ff3118 Iustin Pop
        raise
264 73ff3118 Iustin Pop
      else:
265 73ff3118 Iustin Pop
        Log("Idempotent %s failed, retry #%d/%d: %s" %
266 73ff3118 Iustin Pop
            (msg, MAX_RETRIES - retry_count + 1, MAX_RETRIES, err))
267 73ff3118 Iustin Pop
        self.MaybeRetry(retry_count - 1, msg, fn, *args)
268 73ff3118 Iustin Pop
269 73ff3118 Iustin Pop
  def _ExecOp(self, *ops):
270 c723c163 Iustin Pop
    """Execute one or more opcodes and manage the exec buffer.
271 c723c163 Iustin Pop
272 c723c163 Iustin Pop
    @result: if only opcode has been passed, we return its result;
273 c723c163 Iustin Pop
        otherwise we return the list of results
274 c723c163 Iustin Pop
275 c723c163 Iustin Pop
    """
276 c723c163 Iustin Pop
    job_id = cli.SendJob(ops, cl=self.cl)
277 c723c163 Iustin Pop
    results = cli.PollJob(job_id, cl=self.cl, feedback_fn=self.Feedback)
278 c723c163 Iustin Pop
    if len(ops) == 1:
279 c723c163 Iustin Pop
      return results[0]
280 c723c163 Iustin Pop
    else:
281 c723c163 Iustin Pop
      return results
282 c723c163 Iustin Pop
283 73ff3118 Iustin Pop
  def ExecOp(self, retry, *ops):
284 73ff3118 Iustin Pop
    """Execute one or more opcodes and manage the exec buffer.
285 73ff3118 Iustin Pop
286 73ff3118 Iustin Pop
    @result: if only opcode has been passed, we return its result;
287 73ff3118 Iustin Pop
        otherwise we return the list of results
288 73ff3118 Iustin Pop
289 73ff3118 Iustin Pop
    """
290 73ff3118 Iustin Pop
    if retry:
291 73ff3118 Iustin Pop
      rval = MAX_RETRIES
292 73ff3118 Iustin Pop
    else:
293 73ff3118 Iustin Pop
      rval = 0
294 73ff3118 Iustin Pop
    return self.MaybeRetry(rval, "opcode", self._ExecOp, *ops)
295 73ff3118 Iustin Pop
296 c723c163 Iustin Pop
  def ExecOrQueue(self, name, *ops):
297 21546b1c Iustin Pop
    """Execute an opcode and manage the exec buffer."""
298 c723c163 Iustin Pop
    if self.opts.parallel:
299 c723c163 Iustin Pop
      self.queued_ops.append((ops, name))
300 c723c163 Iustin Pop
    else:
301 73ff3118 Iustin Pop
      return self.ExecOp(self.queue_retry, *ops)
302 73ff3118 Iustin Pop
303 73ff3118 Iustin Pop
  def StartBatch(self, retry):
304 73ff3118 Iustin Pop
    """Start a new batch of jobs.
305 73ff3118 Iustin Pop
306 73ff3118 Iustin Pop
    @param retry: whether this is a retryable batch
307 73ff3118 Iustin Pop
308 73ff3118 Iustin Pop
    """
309 73ff3118 Iustin Pop
    self.queued_ops = []
310 73ff3118 Iustin Pop
    self.queue_retry = retry
311 c723c163 Iustin Pop
312 c723c163 Iustin Pop
  def CommitQueue(self):
313 c723c163 Iustin Pop
    """Execute all submitted opcodes in case of parallel burnin"""
314 c723c163 Iustin Pop
    if not self.opts.parallel:
315 c723c163 Iustin Pop
      return
316 c723c163 Iustin Pop
317 73ff3118 Iustin Pop
    if self.queue_retry:
318 73ff3118 Iustin Pop
      rval = MAX_RETRIES
319 73ff3118 Iustin Pop
    else:
320 73ff3118 Iustin Pop
      rval = 0
321 73ff3118 Iustin Pop
322 c723c163 Iustin Pop
    try:
323 73ff3118 Iustin Pop
      results = self.MaybeRetry(rval, "jobset", self.ExecJobSet,
324 73ff3118 Iustin Pop
                                self.queued_ops)
325 c723c163 Iustin Pop
    finally:
326 c723c163 Iustin Pop
      self.queued_ops = []
327 c723c163 Iustin Pop
    return results
328 ec5c88dc Iustin Pop
329 ec5c88dc Iustin Pop
  def ExecJobSet(self, jobs):
330 ec5c88dc Iustin Pop
    """Execute a set of jobs and return once all are done.
331 ec5c88dc Iustin Pop
332 ec5c88dc Iustin Pop
    The method will return the list of results, if all jobs are
333 c723c163 Iustin Pop
    successful. Otherwise, OpExecError will be raised from within
334 ec5c88dc Iustin Pop
    cli.py.
335 ec5c88dc Iustin Pop
336 ec5c88dc Iustin Pop
    """
337 ec5c88dc Iustin Pop
    self.ClearFeedbackBuf()
338 c723c163 Iustin Pop
    job_ids = [cli.SendJob(row[0], cl=self.cl) for row in jobs]
339 c723c163 Iustin Pop
    Log("Submitted job ID(s) %s" % ", ".join(job_ids), indent=1)
340 ec5c88dc Iustin Pop
    results = []
341 c723c163 Iustin Pop
    for jid, (_, iname) in zip(job_ids, jobs):
342 c723c163 Iustin Pop
      Log("waiting for job %s for %s" % (jid, iname), indent=2)
343 73ff3118 Iustin Pop
      try:
344 73ff3118 Iustin Pop
        results.append(cli.PollJob(jid, cl=self.cl, feedback_fn=self.Feedback))
345 73ff3118 Iustin Pop
      except Exception, err:
346 73ff3118 Iustin Pop
        Log("Job for %s failed: %s" % (iname, err))
347 73ff3118 Iustin Pop
    if len(results) != len(jobs):
348 73ff3118 Iustin Pop
      raise BurninFailure()
349 ec5c88dc Iustin Pop
    return results
350 21546b1c Iustin Pop
351 d9b7a0b4 Iustin Pop
  def _DoCheckInstances(fn):
352 d9b7a0b4 Iustin Pop
    """Decorator for checking instances.
353 d9b7a0b4 Iustin Pop
354 d9b7a0b4 Iustin Pop
    """
355 d9b7a0b4 Iustin Pop
    def wrapper(self, *args, **kwargs):
356 d9b7a0b4 Iustin Pop
      val = fn(self, *args, **kwargs)
357 d9b7a0b4 Iustin Pop
      for instance in self.instances:
358 d9b7a0b4 Iustin Pop
        self._CheckInstanceAlive(instance)
359 d9b7a0b4 Iustin Pop
      return val
360 d9b7a0b4 Iustin Pop
361 d9b7a0b4 Iustin Pop
    return wrapper
362 d9b7a0b4 Iustin Pop
363 c70481ab Iustin Pop
  def _DoBatch(retry):
364 c70481ab Iustin Pop
    """Decorator for possible batch operations.
365 c70481ab Iustin Pop
366 c70481ab Iustin Pop
    Must come after the _DoCheckInstances decorator (if any).
367 c70481ab Iustin Pop
368 c70481ab Iustin Pop
    @param retry: whether this is a retryable batch, will be
369 c70481ab Iustin Pop
        passed to StartBatch
370 c70481ab Iustin Pop
371 c70481ab Iustin Pop
    """
372 c70481ab Iustin Pop
    def wrap(fn):
373 c70481ab Iustin Pop
      def batched(self, *args, **kwargs):
374 c70481ab Iustin Pop
        self.StartBatch(retry)
375 c70481ab Iustin Pop
        val = fn(self, *args, **kwargs)
376 c70481ab Iustin Pop
        self.CommitQueue()
377 c70481ab Iustin Pop
        return val
378 c70481ab Iustin Pop
      return batched
379 c70481ab Iustin Pop
380 c70481ab Iustin Pop
    return wrap
381 c70481ab Iustin Pop
382 175f44c2 Iustin Pop
  def ParseOptions(self):
383 175f44c2 Iustin Pop
    """Parses the command line options.
384 175f44c2 Iustin Pop
385 175f44c2 Iustin Pop
    In case of command line errors, it will show the usage and exit the
386 175f44c2 Iustin Pop
    program.
387 175f44c2 Iustin Pop
388 175f44c2 Iustin Pop
    """
389 175f44c2 Iustin Pop
    parser = optparse.OptionParser(usage="\n%s" % USAGE,
390 10eb54fb Michael Hanselmann
                                   version=("%%prog (ganeti) %s" %
391 10eb54fb Michael Hanselmann
                                            constants.RELEASE_VERSION),
392 10eb54fb Michael Hanselmann
                                   option_list=OPTIONS)
393 175f44c2 Iustin Pop
394 175f44c2 Iustin Pop
    options, args = parser.parse_args()
395 175f44c2 Iustin Pop
    if len(args) < 1 or options.os is None:
396 175f44c2 Iustin Pop
      Usage()
397 175f44c2 Iustin Pop
398 f9193417 Iustin Pop
    supported_disk_templates = (constants.DT_DISKLESS,
399 2f505cb5 Manuel Franceschini
                                constants.DT_FILE,
400 f9193417 Iustin Pop
                                constants.DT_PLAIN,
401 12c3449a Michael Hanselmann
                                constants.DT_DRBD8)
402 12c3449a Michael Hanselmann
    if options.disk_template not in supported_disk_templates:
403 836d59d7 Iustin Pop
      Err("Unknown disk template '%s'" % options.disk_template)
404 175f44c2 Iustin Pop
405 b518a14a Iustin Pop
    if options.disk_template == constants.DT_DISKLESS:
406 b518a14a Iustin Pop
      disk_size = disk_growth = []
407 5178f1bc Iustin Pop
      options.do_addremove_disks = False
408 b518a14a Iustin Pop
    else:
409 b518a14a Iustin Pop
      disk_size = [utils.ParseUnit(v) for v in options.disk_size.split(",")]
410 b518a14a Iustin Pop
      disk_growth = [utils.ParseUnit(v)
411 b518a14a Iustin Pop
                     for v in options.disk_growth.split(",")]
412 b518a14a Iustin Pop
      if len(disk_growth) != len(disk_size):
413 836d59d7 Iustin Pop
        Err("Wrong disk sizes/growth combination")
414 08db7c5c Iustin Pop
    if ((disk_size and options.disk_template == constants.DT_DISKLESS) or
415 08db7c5c Iustin Pop
        (not disk_size and options.disk_template != constants.DT_DISKLESS)):
416 836d59d7 Iustin Pop
      Err("Wrong disk count/disk template combination")
417 08db7c5c Iustin Pop
418 08db7c5c Iustin Pop
    self.disk_size = disk_size
419 08db7c5c Iustin Pop
    self.disk_growth = disk_growth
420 08db7c5c Iustin Pop
    self.disk_count = len(disk_size)
421 08db7c5c Iustin Pop
422 b91bde14 Iustin Pop
    if options.nodes and options.iallocator:
423 836d59d7 Iustin Pop
      Err("Give either the nodes option or the iallocator option, not both")
424 b91bde14 Iustin Pop
425 175f44c2 Iustin Pop
    self.opts = options
426 175f44c2 Iustin Pop
    self.instances = args
427 338e51e8 Iustin Pop
    self.bep = {
428 338e51e8 Iustin Pop
      constants.BE_MEMORY: options.mem_size,
429 338e51e8 Iustin Pop
      constants.BE_VCPUS: 1,
430 338e51e8 Iustin Pop
      }
431 338e51e8 Iustin Pop
    self.hvp = {}
432 175f44c2 Iustin Pop
433 5178f1bc Iustin Pop
    socket.setdefaulttimeout(options.net_timeout)
434 5178f1bc Iustin Pop
435 175f44c2 Iustin Pop
  def GetState(self):
436 175f44c2 Iustin Pop
    """Read the cluster state from the config."""
437 175f44c2 Iustin Pop
    if self.opts.nodes:
438 175f44c2 Iustin Pop
      names = self.opts.nodes.split(",")
439 175f44c2 Iustin Pop
    else:
440 175f44c2 Iustin Pop
      names = []
441 175f44c2 Iustin Pop
    try:
442 e8d47209 Iustin Pop
      op = opcodes.OpQueryNodes(output_fields=["name", "offline", "drained"],
443 ec79568d Iustin Pop
                                names=names, use_locking=True)
444 73ff3118 Iustin Pop
      result = self.ExecOp(True, op)
445 175f44c2 Iustin Pop
    except errors.GenericError, err:
446 175f44c2 Iustin Pop
      err_code, msg = cli.FormatError(err)
447 836d59d7 Iustin Pop
      Err(msg, exit_code=err_code)
448 e8d47209 Iustin Pop
    self.nodes = [data[0] for data in result if not (data[1] or data[2])]
449 175f44c2 Iustin Pop
450 e3ac208c Guido Trotter
    op_diagnose = opcodes.OpDiagnoseOS(output_fields=["name", "valid",
451 e3ac208c Guido Trotter
                                                      "variants"], names=[])
452 e3ac208c Guido Trotter
    result = self.ExecOp(True, op_diagnose)
453 175f44c2 Iustin Pop
454 175f44c2 Iustin Pop
    if not result:
455 836d59d7 Iustin Pop
      Err("Can't get the OS list")
456 175f44c2 Iustin Pop
457 e3ac208c Guido Trotter
    found = False
458 e3ac208c Guido Trotter
    for (name, valid, variants) in result:
459 e3ac208c Guido Trotter
      if valid and self.opts.os in cli.CalculateOSNames(name, variants):
460 e3ac208c Guido Trotter
        found = True
461 e3ac208c Guido Trotter
        break
462 175f44c2 Iustin Pop
463 e3ac208c Guido Trotter
    if not found:
464 836d59d7 Iustin Pop
      Err("OS '%s' not found" % self.opts.os)
465 175f44c2 Iustin Pop
466 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
467 c70481ab Iustin Pop
  @_DoBatch(False)
468 c723c163 Iustin Pop
  def BurnCreateInstances(self):
469 175f44c2 Iustin Pop
    """Create the given instances.
470 175f44c2 Iustin Pop
471 175f44c2 Iustin Pop
    """
472 175f44c2 Iustin Pop
    self.to_rem = []
473 175f44c2 Iustin Pop
    mytor = izip(cycle(self.nodes),
474 175f44c2 Iustin Pop
                 islice(cycle(self.nodes), 1, None),
475 175f44c2 Iustin Pop
                 self.instances)
476 338e51e8 Iustin Pop
477 836d59d7 Iustin Pop
    Log("Creating instances")
478 175f44c2 Iustin Pop
    for pnode, snode, instance in mytor:
479 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
480 b91bde14 Iustin Pop
      if self.opts.iallocator:
481 b91bde14 Iustin Pop
        pnode = snode = None
482 836d59d7 Iustin Pop
        msg = "with iallocator %s" % self.opts.iallocator
483 6d54548e Guido Trotter
      elif self.opts.disk_template not in constants.DTS_NET_MIRROR:
484 6d54548e Guido Trotter
        snode = None
485 836d59d7 Iustin Pop
        msg = "on %s" % pnode
486 6d54548e Guido Trotter
      else:
487 836d59d7 Iustin Pop
        msg = "on %s, %s" % (pnode, snode)
488 836d59d7 Iustin Pop
489 836d59d7 Iustin Pop
      Log(msg, indent=2)
490 6d54548e Guido Trotter
491 175f44c2 Iustin Pop
      op = opcodes.OpCreateInstance(instance_name=instance,
492 08db7c5c Iustin Pop
                                    disks = [ {"size": size}
493 08db7c5c Iustin Pop
                                              for size in self.disk_size],
494 175f44c2 Iustin Pop
                                    disk_template=self.opts.disk_template,
495 b518a14a Iustin Pop
                                    nics=self.opts.nics,
496 a8083063 Iustin Pop
                                    mode=constants.INSTANCE_CREATE,
497 175f44c2 Iustin Pop
                                    os_type=self.opts.os,
498 175f44c2 Iustin Pop
                                    pnode=pnode,
499 175f44c2 Iustin Pop
                                    snode=snode,
500 a8083063 Iustin Pop
                                    start=True,
501 e9f745aa Iustin Pop
                                    ip_check=True,
502 4501af56 Iustin Pop
                                    wait_for_sync=True,
503 2f505cb5 Manuel Franceschini
                                    file_driver="loop",
504 b91bde14 Iustin Pop
                                    file_storage_dir=None,
505 6e2dc934 Alexander Schreiber
                                    iallocator=self.opts.iallocator,
506 338e51e8 Iustin Pop
                                    beparams=self.bep,
507 338e51e8 Iustin Pop
                                    hvparams=self.hvp,
508 338e51e8 Iustin Pop
                                    )
509 6e2dc934 Alexander Schreiber
510 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op)
511 c723c163 Iustin Pop
      self.to_rem.append(instance)
512 c723c163 Iustin Pop
513 c70481ab Iustin Pop
  @_DoBatch(False)
514 c723c163 Iustin Pop
  def BurnGrowDisks(self):
515 659712c8 Iustin Pop
    """Grow both the os and the swap disks by the requested amount, if any."""
516 836d59d7 Iustin Pop
    Log("Growing disks")
517 659712c8 Iustin Pop
    for instance in self.instances:
518 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
519 08db7c5c Iustin Pop
      for idx, growth in enumerate(self.disk_growth):
520 659712c8 Iustin Pop
        if growth > 0:
521 08db7c5c Iustin Pop
          op = opcodes.OpGrowDisk(instance_name=instance, disk=idx,
522 6605411d Iustin Pop
                                  amount=growth, wait_for_sync=True)
523 836d59d7 Iustin Pop
          Log("increase disk/%s by %s MB" % (idx, growth), indent=2)
524 c723c163 Iustin Pop
          self.ExecOrQueue(instance, op)
525 659712c8 Iustin Pop
526 c70481ab Iustin Pop
  @_DoBatch(True)
527 c723c163 Iustin Pop
  def BurnReplaceDisks1D8(self):
528 175f44c2 Iustin Pop
    """Replace disks on primary and secondary for drbd8."""
529 836d59d7 Iustin Pop
    Log("Replacing disks on the same nodes")
530 175f44c2 Iustin Pop
    for instance in self.instances:
531 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
532 c723c163 Iustin Pop
      ops = []
533 175f44c2 Iustin Pop
      for mode in constants.REPLACE_DISK_SEC, constants.REPLACE_DISK_PRI:
534 175f44c2 Iustin Pop
        op = opcodes.OpReplaceDisks(instance_name=instance,
535 175f44c2 Iustin Pop
                                    mode=mode,
536 08db7c5c Iustin Pop
                                    disks=[i for i in range(self.disk_count)])
537 836d59d7 Iustin Pop
        Log("run %s" % mode, indent=2)
538 c723c163 Iustin Pop
        ops.append(op)
539 c723c163 Iustin Pop
      self.ExecOrQueue(instance, *ops)
540 175f44c2 Iustin Pop
541 c70481ab Iustin Pop
  @_DoBatch(True)
542 c723c163 Iustin Pop
  def BurnReplaceDisks2(self):
543 175f44c2 Iustin Pop
    """Replace secondary node."""
544 836d59d7 Iustin Pop
    Log("Changing the secondary node")
545 cfacfd6e Iustin Pop
    mode = constants.REPLACE_DISK_CHG
546 175f44c2 Iustin Pop
547 175f44c2 Iustin Pop
    mytor = izip(islice(cycle(self.nodes), 2, None),
548 175f44c2 Iustin Pop
                 self.instances)
549 175f44c2 Iustin Pop
    for tnode, instance in mytor:
550 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
551 b6e82a65 Iustin Pop
      if self.opts.iallocator:
552 b6e82a65 Iustin Pop
        tnode = None
553 836d59d7 Iustin Pop
        msg = "with iallocator %s" % self.opts.iallocator
554 836d59d7 Iustin Pop
      else:
555 836d59d7 Iustin Pop
        msg = tnode
556 175f44c2 Iustin Pop
      op = opcodes.OpReplaceDisks(instance_name=instance,
557 175f44c2 Iustin Pop
                                  mode=mode,
558 175f44c2 Iustin Pop
                                  remote_node=tnode,
559 b6e82a65 Iustin Pop
                                  iallocator=self.opts.iallocator,
560 ed2fe731 Michael Hanselmann
                                  disks=[])
561 836d59d7 Iustin Pop
      Log("run %s %s" % (mode, msg), indent=2)
562 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op)
563 175f44c2 Iustin Pop
564 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
565 c70481ab Iustin Pop
  @_DoBatch(False)
566 c723c163 Iustin Pop
  def BurnFailover(self):
567 175f44c2 Iustin Pop
    """Failover the instances."""
568 836d59d7 Iustin Pop
    Log("Failing over instances")
569 175f44c2 Iustin Pop
    for instance in self.instances:
570 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
571 175f44c2 Iustin Pop
      op = opcodes.OpFailoverInstance(instance_name=instance,
572 175f44c2 Iustin Pop
                                      ignore_consistency=False)
573 5b9107ff Iustin Pop
      self.ExecOrQueue(instance, op)
574 175f44c2 Iustin Pop
575 5b9107ff Iustin Pop
  @_DoCheckInstances
576 5b9107ff Iustin Pop
  @_DoBatch(False)
577 5b9107ff Iustin Pop
  def BurnMove(self):
578 5b9107ff Iustin Pop
    """Move the instances."""
579 5b9107ff Iustin Pop
    Log("Moving instances")
580 5b9107ff Iustin Pop
    mytor = izip(islice(cycle(self.nodes), 1, None),
581 5b9107ff Iustin Pop
                 self.instances)
582 5b9107ff Iustin Pop
    for tnode, instance in mytor:
583 5b9107ff Iustin Pop
      Log("instance %s" % instance, indent=1)
584 5b9107ff Iustin Pop
      op = opcodes.OpMoveInstance(instance_name=instance,
585 5b9107ff Iustin Pop
                                  target_node=tnode)
586 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op)
587 175f44c2 Iustin Pop
588 c70481ab Iustin Pop
  @_DoBatch(False)
589 c723c163 Iustin Pop
  def BurnMigrate(self):
590 99bdd139 Iustin Pop
    """Migrate the instances."""
591 801cda94 Iustin Pop
    Log("Migrating instances")
592 99bdd139 Iustin Pop
    for instance in self.instances:
593 801cda94 Iustin Pop
      Log("instance %s" % instance, indent=1)
594 c723c163 Iustin Pop
      op1 = opcodes.OpMigrateInstance(instance_name=instance, live=True,
595 c723c163 Iustin Pop
                                      cleanup=False)
596 99bdd139 Iustin Pop
597 c723c163 Iustin Pop
      op2 = opcodes.OpMigrateInstance(instance_name=instance, live=True,
598 c723c163 Iustin Pop
                                      cleanup=True)
599 c723c163 Iustin Pop
      Log("migration and migration cleanup", indent=2)
600 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op1, op2)
601 99bdd139 Iustin Pop
602 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
603 c70481ab Iustin Pop
  @_DoBatch(False)
604 c723c163 Iustin Pop
  def BurnImportExport(self):
605 bd5e77f9 Guido Trotter
    """Export the instance, delete it, and import it back.
606 bd5e77f9 Guido Trotter
607 bd5e77f9 Guido Trotter
    """
608 836d59d7 Iustin Pop
    Log("Exporting and re-importing instances")
609 bd5e77f9 Guido Trotter
    mytor = izip(cycle(self.nodes),
610 bd5e77f9 Guido Trotter
                 islice(cycle(self.nodes), 1, None),
611 bd5e77f9 Guido Trotter
                 islice(cycle(self.nodes), 2, None),
612 bd5e77f9 Guido Trotter
                 self.instances)
613 bd5e77f9 Guido Trotter
614 bd5e77f9 Guido Trotter
    for pnode, snode, enode, instance in mytor:
615 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
616 c723c163 Iustin Pop
      # read the full name of the instance
617 c723c163 Iustin Pop
      nam_op = opcodes.OpQueryInstances(output_fields=["name"],
618 ec79568d Iustin Pop
                                        names=[instance], use_locking=True)
619 73ff3118 Iustin Pop
      full_name = self.ExecOp(False, nam_op)[0][0]
620 c723c163 Iustin Pop
621 f9af35c8 Guido Trotter
      if self.opts.iallocator:
622 f9af35c8 Guido Trotter
        pnode = snode = None
623 836d59d7 Iustin Pop
        import_log_msg = ("import from %s"
624 836d59d7 Iustin Pop
                          " with iallocator %s" %
625 836d59d7 Iustin Pop
                          (enode, self.opts.iallocator))
626 f9af35c8 Guido Trotter
      elif self.opts.disk_template not in constants.DTS_NET_MIRROR:
627 f9af35c8 Guido Trotter
        snode = None
628 836d59d7 Iustin Pop
        import_log_msg = ("import from %s to %s" %
629 836d59d7 Iustin Pop
                          (enode, pnode))
630 f9af35c8 Guido Trotter
      else:
631 836d59d7 Iustin Pop
        import_log_msg = ("import from %s to %s, %s" %
632 836d59d7 Iustin Pop
                          (enode, pnode, snode))
633 f9af35c8 Guido Trotter
634 bd5e77f9 Guido Trotter
      exp_op = opcodes.OpExportInstance(instance_name=instance,
635 bd5e77f9 Guido Trotter
                                           target_node=enode,
636 bd5e77f9 Guido Trotter
                                           shutdown=True)
637 5c54b832 Iustin Pop
      rem_op = opcodes.OpRemoveInstance(instance_name=instance,
638 5c54b832 Iustin Pop
                                        ignore_failures=True)
639 bd5e77f9 Guido Trotter
      imp_dir = os.path.join(constants.EXPORT_DIR, full_name)
640 bd5e77f9 Guido Trotter
      imp_op = opcodes.OpCreateInstance(instance_name=instance,
641 0ca35d45 Guido Trotter
                                        disks = [ {"size": size}
642 0ca35d45 Guido Trotter
                                                  for size in self.disk_size],
643 bd5e77f9 Guido Trotter
                                        disk_template=self.opts.disk_template,
644 b518a14a Iustin Pop
                                        nics=self.opts.nics,
645 bd5e77f9 Guido Trotter
                                        mode=constants.INSTANCE_IMPORT,
646 bd5e77f9 Guido Trotter
                                        src_node=enode,
647 bd5e77f9 Guido Trotter
                                        src_path=imp_dir,
648 bd5e77f9 Guido Trotter
                                        pnode=pnode,
649 bd5e77f9 Guido Trotter
                                        snode=snode,
650 bd5e77f9 Guido Trotter
                                        start=True,
651 bd5e77f9 Guido Trotter
                                        ip_check=True,
652 bd5e77f9 Guido Trotter
                                        wait_for_sync=True,
653 96bb2f71 Manuel Franceschini
                                        file_storage_dir=None,
654 0ca35d45 Guido Trotter
                                        file_driver="loop",
655 6e2dc934 Alexander Schreiber
                                        iallocator=self.opts.iallocator,
656 338e51e8 Iustin Pop
                                        beparams=self.bep,
657 338e51e8 Iustin Pop
                                        hvparams=self.hvp,
658 338e51e8 Iustin Pop
                                        )
659 6e2dc934 Alexander Schreiber
660 4a7ff493 Guido Trotter
      erem_op = opcodes.OpRemoveExport(instance_name=instance)
661 bd5e77f9 Guido Trotter
662 836d59d7 Iustin Pop
      Log("export to node %s" % enode, indent=2)
663 836d59d7 Iustin Pop
      Log("remove instance", indent=2)
664 836d59d7 Iustin Pop
      Log(import_log_msg, indent=2)
665 836d59d7 Iustin Pop
      Log("remove export", indent=2)
666 c723c163 Iustin Pop
      self.ExecOrQueue(instance, exp_op, rem_op, imp_op, erem_op)
667 bd5e77f9 Guido Trotter
668 c723c163 Iustin Pop
  def StopInstanceOp(self, instance):
669 054a8696 Manuel Franceschini
    """Stop given instance."""
670 c723c163 Iustin Pop
    return opcodes.OpShutdownInstance(instance_name=instance)
671 054a8696 Manuel Franceschini
672 c723c163 Iustin Pop
  def StartInstanceOp(self, instance):
673 054a8696 Manuel Franceschini
    """Start given instance."""
674 c723c163 Iustin Pop
    return opcodes.OpStartupInstance(instance_name=instance, force=False)
675 054a8696 Manuel Franceschini
676 c723c163 Iustin Pop
  def RenameInstanceOp(self, instance, instance_new):
677 054a8696 Manuel Franceschini
    """Rename instance."""
678 c723c163 Iustin Pop
    return opcodes.OpRenameInstance(instance_name=instance,
679 c723c163 Iustin Pop
                                    new_name=instance_new)
680 054a8696 Manuel Franceschini
681 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
682 c70481ab Iustin Pop
  @_DoBatch(True)
683 c723c163 Iustin Pop
  def BurnStopStart(self):
684 175f44c2 Iustin Pop
    """Stop/start the instances."""
685 836d59d7 Iustin Pop
    Log("Stopping and starting instances")
686 175f44c2 Iustin Pop
    for instance in self.instances:
687 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
688 c723c163 Iustin Pop
      op1 = self.StopInstanceOp(instance)
689 c723c163 Iustin Pop
      op2 = self.StartInstanceOp(instance)
690 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op1, op2)
691 c723c163 Iustin Pop
692 c70481ab Iustin Pop
  @_DoBatch(False)
693 c723c163 Iustin Pop
  def BurnRemove(self):
694 175f44c2 Iustin Pop
    """Remove the instances."""
695 836d59d7 Iustin Pop
    Log("Removing instances")
696 175f44c2 Iustin Pop
    for instance in self.to_rem:
697 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
698 5c54b832 Iustin Pop
      op = opcodes.OpRemoveInstance(instance_name=instance,
699 5c54b832 Iustin Pop
                                    ignore_failures=True)
700 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op)
701 c723c163 Iustin Pop
702 c723c163 Iustin Pop
  def BurnRename(self):
703 c723c163 Iustin Pop
    """Rename the instances.
704 175f44c2 Iustin Pop
705 c723c163 Iustin Pop
    Note that this function will not execute in parallel, since we
706 c723c163 Iustin Pop
    only have one target for rename.
707 c723c163 Iustin Pop
708 c723c163 Iustin Pop
    """
709 836d59d7 Iustin Pop
    Log("Renaming instances")
710 054a8696 Manuel Franceschini
    rename = self.opts.rename
711 054a8696 Manuel Franceschini
    for instance in self.instances:
712 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
713 2e39ab98 Iustin Pop
      op_stop1 = self.StopInstanceOp(instance)
714 2e39ab98 Iustin Pop
      op_stop2 = self.StopInstanceOp(rename)
715 c723c163 Iustin Pop
      op_rename1 = self.RenameInstanceOp(instance, rename)
716 c723c163 Iustin Pop
      op_rename2 = self.RenameInstanceOp(rename, instance)
717 c723c163 Iustin Pop
      op_start1 = self.StartInstanceOp(rename)
718 c723c163 Iustin Pop
      op_start2 = self.StartInstanceOp(instance)
719 73ff3118 Iustin Pop
      self.ExecOp(False, op_stop1, op_rename1, op_start1)
720 5178f1bc Iustin Pop
      self._CheckInstanceAlive(rename)
721 73ff3118 Iustin Pop
      self.ExecOp(False, op_stop2, op_rename2, op_start2)
722 5178f1bc Iustin Pop
      self._CheckInstanceAlive(instance)
723 5178f1bc Iustin Pop
724 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
725 c70481ab Iustin Pop
  @_DoBatch(True)
726 c723c163 Iustin Pop
  def BurnReinstall(self):
727 00f91f29 Iustin Pop
    """Reinstall the instances."""
728 836d59d7 Iustin Pop
    Log("Reinstalling instances")
729 00f91f29 Iustin Pop
    for instance in self.instances:
730 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
731 c723c163 Iustin Pop
      op1 = self.StopInstanceOp(instance)
732 c723c163 Iustin Pop
      op2 = opcodes.OpReinstallInstance(instance_name=instance)
733 836d59d7 Iustin Pop
      Log("reinstall without passing the OS", indent=2)
734 c723c163 Iustin Pop
      op3 = opcodes.OpReinstallInstance(instance_name=instance,
735 c723c163 Iustin Pop
                                        os_type=self.opts.os)
736 836d59d7 Iustin Pop
      Log("reinstall specifying the OS", indent=2)
737 c723c163 Iustin Pop
      op4 = self.StartInstanceOp(instance)
738 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op1, op2, op3, op4)
739 c723c163 Iustin Pop
740 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
741 c70481ab Iustin Pop
  @_DoBatch(True)
742 c723c163 Iustin Pop
  def BurnReboot(self):
743 836d59d7 Iustin Pop
    """Reboot the instances."""
744 836d59d7 Iustin Pop
    Log("Rebooting instances")
745 00f91f29 Iustin Pop
    for instance in self.instances:
746 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
747 c723c163 Iustin Pop
      ops = []
748 00f91f29 Iustin Pop
      for reboot_type in constants.REBOOT_TYPES:
749 00f91f29 Iustin Pop
        op = opcodes.OpRebootInstance(instance_name=instance,
750 00f91f29 Iustin Pop
                                      reboot_type=reboot_type,
751 00f91f29 Iustin Pop
                                      ignore_secondaries=False)
752 836d59d7 Iustin Pop
        Log("reboot with type '%s'" % reboot_type, indent=2)
753 c723c163 Iustin Pop
        ops.append(op)
754 c723c163 Iustin Pop
      self.ExecOrQueue(instance, *ops)
755 c723c163 Iustin Pop
756 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
757 c70481ab Iustin Pop
  @_DoBatch(True)
758 c723c163 Iustin Pop
  def BurnActivateDisks(self):
759 90e722d1 Iustin Pop
    """Activate and deactivate disks of the instances."""
760 836d59d7 Iustin Pop
    Log("Activating/deactivating disks")
761 90e722d1 Iustin Pop
    for instance in self.instances:
762 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
763 c723c163 Iustin Pop
      op_start = self.StartInstanceOp(instance)
764 90e722d1 Iustin Pop
      op_act = opcodes.OpActivateInstanceDisks(instance_name=instance)
765 90e722d1 Iustin Pop
      op_deact = opcodes.OpDeactivateInstanceDisks(instance_name=instance)
766 c723c163 Iustin Pop
      op_stop = self.StopInstanceOp(instance)
767 836d59d7 Iustin Pop
      Log("activate disks when online", indent=2)
768 836d59d7 Iustin Pop
      Log("activate disks when offline", indent=2)
769 836d59d7 Iustin Pop
      Log("deactivate disks (when offline)", indent=2)
770 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op_act, op_stop, op_act, op_deact, op_start)
771 90e722d1 Iustin Pop
772 d9b7a0b4 Iustin Pop
  @_DoCheckInstances
773 c70481ab Iustin Pop
  @_DoBatch(False)
774 c723c163 Iustin Pop
  def BurnAddRemoveDisks(self):
775 5c22d16e Iustin Pop
    """Add and remove an extra disk for the instances."""
776 836d59d7 Iustin Pop
    Log("Adding and removing disks")
777 5c22d16e Iustin Pop
    for instance in self.instances:
778 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
779 5c22d16e Iustin Pop
      op_add = opcodes.OpSetInstanceParams(\
780 5c22d16e Iustin Pop
        instance_name=instance,
781 5c22d16e Iustin Pop
        disks=[(constants.DDM_ADD, {"size": self.disk_size[0]})])
782 5c22d16e Iustin Pop
      op_rem = opcodes.OpSetInstanceParams(\
783 5c22d16e Iustin Pop
        instance_name=instance, disks=[(constants.DDM_REMOVE, {})])
784 c723c163 Iustin Pop
      op_stop = self.StopInstanceOp(instance)
785 c723c163 Iustin Pop
      op_start = self.StartInstanceOp(instance)
786 836d59d7 Iustin Pop
      Log("adding a disk", indent=2)
787 836d59d7 Iustin Pop
      Log("removing last disk", indent=2)
788 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op_add, op_stop, op_rem, op_start)
789 5c22d16e Iustin Pop
790 c70481ab Iustin Pop
  @_DoBatch(False)
791 c723c163 Iustin Pop
  def BurnAddRemoveNICs(self):
792 5c22d16e Iustin Pop
    """Add and remove an extra NIC for the instances."""
793 836d59d7 Iustin Pop
    Log("Adding and removing NICs")
794 5c22d16e Iustin Pop
    for instance in self.instances:
795 836d59d7 Iustin Pop
      Log("instance %s" % instance, indent=1)
796 5c22d16e Iustin Pop
      op_add = opcodes.OpSetInstanceParams(\
797 5c22d16e Iustin Pop
        instance_name=instance, nics=[(constants.DDM_ADD, {})])
798 5c22d16e Iustin Pop
      op_rem = opcodes.OpSetInstanceParams(\
799 5c22d16e Iustin Pop
        instance_name=instance, nics=[(constants.DDM_REMOVE, {})])
800 836d59d7 Iustin Pop
      Log("adding a NIC", indent=2)
801 836d59d7 Iustin Pop
      Log("removing last NIC", indent=2)
802 c723c163 Iustin Pop
      self.ExecOrQueue(instance, op_add, op_rem)
803 5c22d16e Iustin Pop
804 5178f1bc Iustin Pop
  def _CheckInstanceAlive(self, instance):
805 5178f1bc Iustin Pop
    """Check if an instance is alive by doing http checks.
806 5178f1bc Iustin Pop
807 5178f1bc Iustin Pop
    This will try to retrieve the url on the instance /hostname.txt
808 5178f1bc Iustin Pop
    and check that it contains the hostname of the instance. In case
809 5178f1bc Iustin Pop
    we get ECONNREFUSED, we retry up to the net timeout seconds, for
810 5178f1bc Iustin Pop
    any other error we abort.
811 5178f1bc Iustin Pop
812 5178f1bc Iustin Pop
    """
813 5178f1bc Iustin Pop
    if not self.opts.http_check:
814 5178f1bc Iustin Pop
      return
815 5dc626fd Iustin Pop
    end_time = time.time() + self.opts.net_timeout
816 5dc626fd Iustin Pop
    url = None
817 5dc626fd Iustin Pop
    while time.time() < end_time and url is None:
818 5dc626fd Iustin Pop
      try:
819 5dc626fd Iustin Pop
        url = self.url_opener.open("http://%s/hostname.txt" % instance)
820 c723c163 Iustin Pop
      except IOError:
821 5dc626fd Iustin Pop
        # here we can have connection refused, no route to host, etc.
822 5dc626fd Iustin Pop
        time.sleep(1)
823 5dc626fd Iustin Pop
    if url is None:
824 5dc626fd Iustin Pop
      raise InstanceDown(instance, "Cannot contact instance")
825 5178f1bc Iustin Pop
    hostname = url.read().strip()
826 5dc626fd Iustin Pop
    url.close()
827 5178f1bc Iustin Pop
    if hostname != instance:
828 5178f1bc Iustin Pop
      raise InstanceDown(instance, ("Hostname mismatch, expected %s, got %s" %
829 5178f1bc Iustin Pop
                                    (instance, hostname)))
830 5178f1bc Iustin Pop
831 175f44c2 Iustin Pop
  def BurninCluster(self):
832 175f44c2 Iustin Pop
    """Test a cluster intensively.
833 175f44c2 Iustin Pop
834 175f44c2 Iustin Pop
    This will create instances and then start/stop/failover them.
835 175f44c2 Iustin Pop
    It is safe for existing instances but could impact performance.
836 175f44c2 Iustin Pop
837 175f44c2 Iustin Pop
    """
838 175f44c2 Iustin Pop
839 175f44c2 Iustin Pop
    opts = self.opts
840 175f44c2 Iustin Pop
841 836d59d7 Iustin Pop
    Log("Testing global parameters")
842 175f44c2 Iustin Pop
843 bd249e2f Iustin Pop
    if (len(self.nodes) == 1 and
844 2f505cb5 Manuel Franceschini
        opts.disk_template not in (constants.DT_DISKLESS, constants.DT_PLAIN,
845 2f505cb5 Manuel Franceschini
                                   constants.DT_FILE)):
846 836d59d7 Iustin Pop
      Err("When one node is available/selected the disk template must"
847 2f505cb5 Manuel Franceschini
          " be 'diskless', 'file' or 'plain'")
848 175f44c2 Iustin Pop
849 21546b1c Iustin Pop
    has_err = True
850 175f44c2 Iustin Pop
    try:
851 c723c163 Iustin Pop
      self.BurnCreateInstances()
852 175f44c2 Iustin Pop
      if opts.do_replace1 and opts.disk_template in constants.DTS_NET_MIRROR:
853 c723c163 Iustin Pop
        self.BurnReplaceDisks1D8()
854 175f44c2 Iustin Pop
      if (opts.do_replace2 and len(self.nodes) > 2 and
855 175f44c2 Iustin Pop
          opts.disk_template in constants.DTS_NET_MIRROR) :
856 c723c163 Iustin Pop
        self.BurnReplaceDisks2()
857 175f44c2 Iustin Pop
858 aa089b65 Iustin Pop
      if (opts.disk_template != constants.DT_DISKLESS and
859 aa089b65 Iustin Pop
          utils.any(self.disk_growth, lambda n: n > 0)):
860 c723c163 Iustin Pop
        self.BurnGrowDisks()
861 659712c8 Iustin Pop
862 175f44c2 Iustin Pop
      if opts.do_failover and opts.disk_template in constants.DTS_NET_MIRROR:
863 c723c163 Iustin Pop
        self.BurnFailover()
864 175f44c2 Iustin Pop
865 99bdd139 Iustin Pop
      if opts.do_migrate and opts.disk_template == constants.DT_DRBD8:
866 c723c163 Iustin Pop
        self.BurnMigrate()
867 99bdd139 Iustin Pop
868 9e32b93b Guido Trotter
      if (opts.do_move and len(self.nodes) > 1 and
869 9e32b93b Guido Trotter
          opts.disk_template in [constants.DT_PLAIN, constants.DT_FILE]):
870 5b9107ff Iustin Pop
        self.BurnMove()
871 5b9107ff Iustin Pop
872 a5e7be6b Iustin Pop
      if (opts.do_importexport and
873 a5e7be6b Iustin Pop
          opts.disk_template not in (constants.DT_DISKLESS,
874 a5e7be6b Iustin Pop
                                     constants.DT_FILE)):
875 c723c163 Iustin Pop
        self.BurnImportExport()
876 bd5e77f9 Guido Trotter
877 00f91f29 Iustin Pop
      if opts.do_reinstall:
878 c723c163 Iustin Pop
        self.BurnReinstall()
879 00f91f29 Iustin Pop
880 00f91f29 Iustin Pop
      if opts.do_reboot:
881 c723c163 Iustin Pop
        self.BurnReboot()
882 00f91f29 Iustin Pop
883 5c22d16e Iustin Pop
      if opts.do_addremove_disks:
884 c723c163 Iustin Pop
        self.BurnAddRemoveDisks()
885 5c22d16e Iustin Pop
886 5c22d16e Iustin Pop
      if opts.do_addremove_nics:
887 c723c163 Iustin Pop
        self.BurnAddRemoveNICs()
888 5c22d16e Iustin Pop
889 90e722d1 Iustin Pop
      if opts.do_activate_disks:
890 c723c163 Iustin Pop
        self.BurnActivateDisks()
891 90e722d1 Iustin Pop
892 054a8696 Manuel Franceschini
      if opts.rename:
893 c723c163 Iustin Pop
        self.BurnRename()
894 054a8696 Manuel Franceschini
895 eb61f8d3 Iustin Pop
      if opts.do_startstop:
896 c723c163 Iustin Pop
        self.BurnStopStart()
897 eb61f8d3 Iustin Pop
898 21546b1c Iustin Pop
      has_err = False
899 175f44c2 Iustin Pop
    finally:
900 21546b1c Iustin Pop
      if has_err:
901 21546b1c Iustin Pop
        Log("Error detected: opcode buffer follows:\n\n")
902 21546b1c Iustin Pop
        Log(self.GetFeedbackBuf())
903 21546b1c Iustin Pop
        Log("\n\n")
904 320eda24 Iustin Pop
      if not self.opts.keep_instances:
905 8629a543 Iustin Pop
        try:
906 8629a543 Iustin Pop
          self.BurnRemove()
907 8629a543 Iustin Pop
        except Exception, err:
908 8629a543 Iustin Pop
          if has_err: # already detected errors, so errors in removal
909 8629a543 Iustin Pop
                      # are quite expected
910 8629a543 Iustin Pop
            Log("Note: error detected during instance remove: %s" % str(err))
911 8629a543 Iustin Pop
          else: # non-expected error
912 8629a543 Iustin Pop
            raise
913 175f44c2 Iustin Pop
914 175f44c2 Iustin Pop
    return 0
915 a8083063 Iustin Pop
916 01b69ec5 Michael Hanselmann
917 a8083063 Iustin Pop
def main():
918 3ecf6786 Iustin Pop
  """Main function"""
919 3ecf6786 Iustin Pop
920 175f44c2 Iustin Pop
  burner = Burner()
921 a4af651e Iustin Pop
  return burner.BurninCluster()
922 a8083063 Iustin Pop
923 01b69ec5 Michael Hanselmann
924 a8083063 Iustin Pop
if __name__ == "__main__":
925 3ecf6786 Iustin Pop
  main()