Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ f4ad2ef0

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