Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ 1b334175

History | View | Annotate | Download (33.3 kB)

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