Statistics
| Branch: | Tag: | Revision:

root / tools / burnin @ c4feafe8

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