Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / base.py @ 6ea039ef

History | View | Annotate | Download (21.5 kB)

1 1a732a74 Thomas Thrainer
#
2 1a732a74 Thomas Thrainer
#
3 1a732a74 Thomas Thrainer
4 1a732a74 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 1a732a74 Thomas Thrainer
#
6 1a732a74 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 1a732a74 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 1a732a74 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 1a732a74 Thomas Thrainer
# (at your option) any later version.
10 1a732a74 Thomas Thrainer
#
11 1a732a74 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 1a732a74 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 1a732a74 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 1a732a74 Thomas Thrainer
# General Public License for more details.
15 1a732a74 Thomas Thrainer
#
16 1a732a74 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 1a732a74 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 1a732a74 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 1a732a74 Thomas Thrainer
# 02110-1301, USA.
20 1a732a74 Thomas Thrainer
21 1a732a74 Thomas Thrainer
22 1a732a74 Thomas Thrainer
"""Base classes and functions for cmdlib."""
23 1a732a74 Thomas Thrainer
24 1a732a74 Thomas Thrainer
import logging
25 1a732a74 Thomas Thrainer
26 1a732a74 Thomas Thrainer
from ganeti import errors
27 1a732a74 Thomas Thrainer
from ganeti import constants
28 1a732a74 Thomas Thrainer
from ganeti import locking
29 1a732a74 Thomas Thrainer
from ganeti import query
30 1a732a74 Thomas Thrainer
from ganeti import utils
31 da4a52a3 Thomas Thrainer
from ganeti.cmdlib.common import ExpandInstanceUuidAndName
32 1a732a74 Thomas Thrainer
33 1a732a74 Thomas Thrainer
34 1a732a74 Thomas Thrainer
class ResultWithJobs:
35 1a732a74 Thomas Thrainer
  """Data container for LU results with jobs.
36 1a732a74 Thomas Thrainer

37 1a732a74 Thomas Thrainer
  Instances of this class returned from L{LogicalUnit.Exec} will be recognized
38 1a732a74 Thomas Thrainer
  by L{mcpu._ProcessResult}. The latter will then submit the jobs
39 1a732a74 Thomas Thrainer
  contained in the C{jobs} attribute and include the job IDs in the opcode
40 1a732a74 Thomas Thrainer
  result.
41 1a732a74 Thomas Thrainer

42 1a732a74 Thomas Thrainer
  """
43 1a732a74 Thomas Thrainer
  def __init__(self, jobs, **kwargs):
44 1a732a74 Thomas Thrainer
    """Initializes this class.
45 1a732a74 Thomas Thrainer

46 1a732a74 Thomas Thrainer
    Additional return values can be specified as keyword arguments.
47 1a732a74 Thomas Thrainer

48 1a732a74 Thomas Thrainer
    @type jobs: list of lists of L{opcode.OpCode}
49 1a732a74 Thomas Thrainer
    @param jobs: A list of lists of opcode objects
50 1a732a74 Thomas Thrainer

51 1a732a74 Thomas Thrainer
    """
52 1a732a74 Thomas Thrainer
    self.jobs = jobs
53 1a732a74 Thomas Thrainer
    self.other = kwargs
54 1a732a74 Thomas Thrainer
55 1a732a74 Thomas Thrainer
56 87ed6b79 Klaus Aehlig
class LUWConfdClient(object):
57 87ed6b79 Klaus Aehlig
  """Wrapper class for wconfd client calls from LUs.
58 87ed6b79 Klaus Aehlig

59 87ed6b79 Klaus Aehlig
  Correctly updates the cache of the LU's owned locks
60 87ed6b79 Klaus Aehlig
  when leaving. Also transparently adds the context
61 87ed6b79 Klaus Aehlig
  for resource requests.
62 87ed6b79 Klaus Aehlig

63 87ed6b79 Klaus Aehlig
  """
64 87ed6b79 Klaus Aehlig
  def __init__(self, lu):
65 87ed6b79 Klaus Aehlig
    self.lu = lu
66 87ed6b79 Klaus Aehlig
67 87ed6b79 Klaus Aehlig
  def TryUpdateLocks(self, req):
68 6ea039ef Petr Pudlak
    self.lu.wconfd.Client().TryUpdateLocks(self.lu.wconfdcontext, req)
69 6ea039ef Petr Pudlak
    self.lu.wconfdlocks = \
70 6ea039ef Petr Pudlak
      self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext)
71 87ed6b79 Klaus Aehlig
72 87ed6b79 Klaus Aehlig
  def DownGradeLocksLevel(self, level):
73 6ea039ef Petr Pudlak
    self.lu.wconfd.Client().DownGradeLocksLevel(self.lu.wconfdcontext, level)
74 6ea039ef Petr Pudlak
    self.lu.wconfdlocks = \
75 6ea039ef Petr Pudlak
      self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext)
76 87ed6b79 Klaus Aehlig
77 87ed6b79 Klaus Aehlig
  def FreeLocksLevel(self, level):
78 6ea039ef Petr Pudlak
    self.lu.wconfd.Client().FreeLocksLevel(self.lu.wconfdcontext, level)
79 6ea039ef Petr Pudlak
    self.lu.wconfdlocks = \
80 6ea039ef Petr Pudlak
      self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext)
81 87ed6b79 Klaus Aehlig
82 87ed6b79 Klaus Aehlig
83 1a732a74 Thomas Thrainer
class LogicalUnit(object):
84 1a732a74 Thomas Thrainer
  """Logical Unit base class.
85 1a732a74 Thomas Thrainer

86 1a732a74 Thomas Thrainer
  Subclasses must follow these rules:
87 1a732a74 Thomas Thrainer
    - implement ExpandNames
88 1a732a74 Thomas Thrainer
    - implement CheckPrereq (except when tasklets are used)
89 1a732a74 Thomas Thrainer
    - implement Exec (except when tasklets are used)
90 1a732a74 Thomas Thrainer
    - implement BuildHooksEnv
91 1a732a74 Thomas Thrainer
    - implement BuildHooksNodes
92 1a732a74 Thomas Thrainer
    - redefine HPATH and HTYPE
93 1a732a74 Thomas Thrainer
    - optionally redefine their run requirements:
94 1a732a74 Thomas Thrainer
        REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
95 1a732a74 Thomas Thrainer

96 1a732a74 Thomas Thrainer
  Note that all commands require root permissions.
97 1a732a74 Thomas Thrainer

98 1a732a74 Thomas Thrainer
  @ivar dry_run_result: the value (if any) that will be returned to the caller
99 1a732a74 Thomas Thrainer
      in dry-run mode (signalled by opcode dry_run parameter)
100 1a732a74 Thomas Thrainer

101 1a732a74 Thomas Thrainer
  """
102 1a732a74 Thomas Thrainer
  HPATH = None
103 1a732a74 Thomas Thrainer
  HTYPE = None
104 1a732a74 Thomas Thrainer
  REQ_BGL = True
105 1a732a74 Thomas Thrainer
106 87ed6b79 Klaus Aehlig
  def __init__(self, processor, op, context, rpc_runner, wconfdcontext, wconfd):
107 1a732a74 Thomas Thrainer
    """Constructor for LogicalUnit.
108 1a732a74 Thomas Thrainer

109 1a732a74 Thomas Thrainer
    This needs to be overridden in derived classes in order to check op
110 1a732a74 Thomas Thrainer
    validity.
111 1a732a74 Thomas Thrainer

112 87ed6b79 Klaus Aehlig
    @type wconfdcontext: (int, string)
113 87ed6b79 Klaus Aehlig
    @param wconfdcontext: the identity of the logical unit to represent itself
114 87ed6b79 Klaus Aehlig
        to wconfd when asking for resources; it is given as job id and livelock
115 87ed6b79 Klaus Aehlig
        file.
116 87ed6b79 Klaus Aehlig
    @param wconfd: the wconfd class to use; dependency injection to allow
117 87ed6b79 Klaus Aehlig
        testability.
118 87ed6b79 Klaus Aehlig

119 1a732a74 Thomas Thrainer
    """
120 1a732a74 Thomas Thrainer
    self.proc = processor
121 1a732a74 Thomas Thrainer
    self.op = op
122 1a732a74 Thomas Thrainer
    self.cfg = context.cfg
123 87ed6b79 Klaus Aehlig
    self.wconfdlocks = []
124 87ed6b79 Klaus Aehlig
    self.wconfdcontext = wconfdcontext
125 1a732a74 Thomas Thrainer
    self.context = context
126 1a732a74 Thomas Thrainer
    self.rpc = rpc_runner
127 87ed6b79 Klaus Aehlig
    self.wconfd = wconfd # wconfd module to use, for testing
128 1a732a74 Thomas Thrainer
129 1a732a74 Thomas Thrainer
    # Dictionaries used to declare locking needs to mcpu
130 1a732a74 Thomas Thrainer
    self.needed_locks = None
131 1a732a74 Thomas Thrainer
    self.share_locks = dict.fromkeys(locking.LEVELS, 0)
132 1a732a74 Thomas Thrainer
    self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False)
133 1a732a74 Thomas Thrainer
134 1a732a74 Thomas Thrainer
    self.add_locks = {}
135 1a732a74 Thomas Thrainer
    self.remove_locks = {}
136 1a732a74 Thomas Thrainer
137 1a732a74 Thomas Thrainer
    # Used to force good behavior when calling helper functions
138 1a732a74 Thomas Thrainer
    self.recalculate_locks = {}
139 1a732a74 Thomas Thrainer
140 1a732a74 Thomas Thrainer
    # logging
141 1a732a74 Thomas Thrainer
    self.Log = processor.Log # pylint: disable=C0103
142 1a732a74 Thomas Thrainer
    self.LogWarning = processor.LogWarning # pylint: disable=C0103
143 1a732a74 Thomas Thrainer
    self.LogInfo = processor.LogInfo # pylint: disable=C0103
144 1a732a74 Thomas Thrainer
    self.LogStep = processor.LogStep # pylint: disable=C0103
145 1a732a74 Thomas Thrainer
    # support for dry-run
146 1a732a74 Thomas Thrainer
    self.dry_run_result = None
147 1a732a74 Thomas Thrainer
    # support for generic debug attribute
148 1a732a74 Thomas Thrainer
    if (not hasattr(self.op, "debug_level") or
149 1a732a74 Thomas Thrainer
        not isinstance(self.op.debug_level, int)):
150 1a732a74 Thomas Thrainer
      self.op.debug_level = 0
151 1a732a74 Thomas Thrainer
152 1a732a74 Thomas Thrainer
    # Tasklets
153 1a732a74 Thomas Thrainer
    self.tasklets = None
154 1a732a74 Thomas Thrainer
155 1a732a74 Thomas Thrainer
    # Validate opcode parameters and set defaults
156 1a732a74 Thomas Thrainer
    self.op.Validate(True)
157 1a732a74 Thomas Thrainer
158 1a732a74 Thomas Thrainer
    self.CheckArguments()
159 1a732a74 Thomas Thrainer
160 87ed6b79 Klaus Aehlig
  def WConfdClient(self):
161 87ed6b79 Klaus Aehlig
    return LUWConfdClient(self)
162 87ed6b79 Klaus Aehlig
163 87ed6b79 Klaus Aehlig
  def owned_locks(self, level):
164 87ed6b79 Klaus Aehlig
    """Return the list of locks owned by the LU at a given level.
165 87ed6b79 Klaus Aehlig

166 87ed6b79 Klaus Aehlig
    This method assumes that is field wconfdlocks is set correctly
167 87ed6b79 Klaus Aehlig
    by mcpu.
168 87ed6b79 Klaus Aehlig

169 87ed6b79 Klaus Aehlig
    """
170 87ed6b79 Klaus Aehlig
    levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
171 87ed6b79 Klaus Aehlig
    locks = set([lock[0][len(levelprefix):]
172 87ed6b79 Klaus Aehlig
                for lock in self.wconfdlocks
173 87ed6b79 Klaus Aehlig
                if lock[0].startswith(levelprefix)])
174 87ed6b79 Klaus Aehlig
    expand_fns = {
175 87ed6b79 Klaus Aehlig
      locking.LEVEL_CLUSTER: (lambda: [locking.BGL]),
176 87ed6b79 Klaus Aehlig
      locking.LEVEL_INSTANCE: self.cfg.GetInstanceList,
177 87ed6b79 Klaus Aehlig
      locking.LEVEL_NODE_ALLOC: (lambda: [locking.NAL]),
178 87ed6b79 Klaus Aehlig
      locking.LEVEL_NODEGROUP: self.cfg.GetNodeGroupList,
179 87ed6b79 Klaus Aehlig
      locking.LEVEL_NODE: self.cfg.GetNodeList,
180 87ed6b79 Klaus Aehlig
      locking.LEVEL_NODE_RES: self.cfg.GetNodeList,
181 87ed6b79 Klaus Aehlig
      locking.LEVEL_NETWORK: self.cfg.GetNetworkList,
182 87ed6b79 Klaus Aehlig
      }
183 87ed6b79 Klaus Aehlig
    if locking.LOCKSET_NAME in locks:
184 87ed6b79 Klaus Aehlig
      return expand_fns[level]()
185 87ed6b79 Klaus Aehlig
    else:
186 87ed6b79 Klaus Aehlig
      return locks
187 87ed6b79 Klaus Aehlig
188 87ed6b79 Klaus Aehlig
  def release_request(self, level, names):
189 87ed6b79 Klaus Aehlig
    """Return a request to release the specified locks of the given level.
190 87ed6b79 Klaus Aehlig

191 87ed6b79 Klaus Aehlig
    Correctly break up the group lock to do so.
192 87ed6b79 Klaus Aehlig

193 87ed6b79 Klaus Aehlig
    """
194 87ed6b79 Klaus Aehlig
    levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
195 87ed6b79 Klaus Aehlig
    release = [[levelprefix + lock, "release"] for lock in names]
196 87ed6b79 Klaus Aehlig
197 87ed6b79 Klaus Aehlig
    # if we break up the set-lock, make sure we ask for the rest of it.
198 87ed6b79 Klaus Aehlig
    setlock = levelprefix + locking.LOCKSET_NAME
199 87ed6b79 Klaus Aehlig
    if [setlock, "exclusive"] in self.wconfdlocks:
200 87ed6b79 Klaus Aehlig
      owned = self.owned_locks(level)
201 87ed6b79 Klaus Aehlig
      request = [[levelprefix + lock, "exclusive"]
202 87ed6b79 Klaus Aehlig
                 for lock in owned
203 87ed6b79 Klaus Aehlig
                 if lock not in names]
204 87ed6b79 Klaus Aehlig
    elif [setlock, "shared"] in self.wconfdlocks:
205 87ed6b79 Klaus Aehlig
      owned = self.owned_locks(level)
206 87ed6b79 Klaus Aehlig
      request = [[levelprefix + lock, "shared"]
207 87ed6b79 Klaus Aehlig
                 for lock in owned
208 87ed6b79 Klaus Aehlig
                 if lock not in names]
209 87ed6b79 Klaus Aehlig
    else:
210 87ed6b79 Klaus Aehlig
      request = []
211 87ed6b79 Klaus Aehlig
212 87ed6b79 Klaus Aehlig
    return release + [[setlock, "release"]] + request
213 87ed6b79 Klaus Aehlig
214 1a732a74 Thomas Thrainer
  def CheckArguments(self):
215 1a732a74 Thomas Thrainer
    """Check syntactic validity for the opcode arguments.
216 1a732a74 Thomas Thrainer

217 1a732a74 Thomas Thrainer
    This method is for doing a simple syntactic check and ensure
218 1a732a74 Thomas Thrainer
    validity of opcode parameters, without any cluster-related
219 1a732a74 Thomas Thrainer
    checks. While the same can be accomplished in ExpandNames and/or
220 1a732a74 Thomas Thrainer
    CheckPrereq, doing these separate is better because:
221 1a732a74 Thomas Thrainer

222 1a732a74 Thomas Thrainer
      - ExpandNames is left as as purely a lock-related function
223 1a732a74 Thomas Thrainer
      - CheckPrereq is run after we have acquired locks (and possible
224 1a732a74 Thomas Thrainer
        waited for them)
225 1a732a74 Thomas Thrainer

226 1a732a74 Thomas Thrainer
    The function is allowed to change the self.op attribute so that
227 1a732a74 Thomas Thrainer
    later methods can no longer worry about missing parameters.
228 1a732a74 Thomas Thrainer

229 1a732a74 Thomas Thrainer
    """
230 1a732a74 Thomas Thrainer
    pass
231 1a732a74 Thomas Thrainer
232 1a732a74 Thomas Thrainer
  def ExpandNames(self):
233 1a732a74 Thomas Thrainer
    """Expand names for this LU.
234 1a732a74 Thomas Thrainer

235 1a732a74 Thomas Thrainer
    This method is called before starting to execute the opcode, and it should
236 1a732a74 Thomas Thrainer
    update all the parameters of the opcode to their canonical form (e.g. a
237 1a732a74 Thomas Thrainer
    short node name must be fully expanded after this method has successfully
238 1a732a74 Thomas Thrainer
    completed). This way locking, hooks, logging, etc. can work correctly.
239 1a732a74 Thomas Thrainer

240 1a732a74 Thomas Thrainer
    LUs which implement this method must also populate the self.needed_locks
241 1a732a74 Thomas Thrainer
    member, as a dict with lock levels as keys, and a list of needed lock names
242 1a732a74 Thomas Thrainer
    as values. Rules:
243 1a732a74 Thomas Thrainer

244 1a732a74 Thomas Thrainer
      - use an empty dict if you don't need any lock
245 1a732a74 Thomas Thrainer
      - if you don't need any lock at a particular level omit that
246 1a732a74 Thomas Thrainer
        level (note that in this case C{DeclareLocks} won't be called
247 1a732a74 Thomas Thrainer
        at all for that level)
248 1a732a74 Thomas Thrainer
      - if you need locks at a level, but you can't calculate it in
249 1a732a74 Thomas Thrainer
        this function, initialise that level with an empty list and do
250 1a732a74 Thomas Thrainer
        further processing in L{LogicalUnit.DeclareLocks} (see that
251 1a732a74 Thomas Thrainer
        function's docstring)
252 1a732a74 Thomas Thrainer
      - don't put anything for the BGL level
253 1a732a74 Thomas Thrainer
      - if you want all locks at a level use L{locking.ALL_SET} as a value
254 1a732a74 Thomas Thrainer

255 1a732a74 Thomas Thrainer
    If you need to share locks (rather than acquire them exclusively) at one
256 1a732a74 Thomas Thrainer
    level you can modify self.share_locks, setting a true value (usually 1) for
257 1a732a74 Thomas Thrainer
    that level. By default locks are not shared.
258 1a732a74 Thomas Thrainer

259 1a732a74 Thomas Thrainer
    This function can also define a list of tasklets, which then will be
260 1a732a74 Thomas Thrainer
    executed in order instead of the usual LU-level CheckPrereq and Exec
261 1a732a74 Thomas Thrainer
    functions, if those are not defined by the LU.
262 1a732a74 Thomas Thrainer

263 1a732a74 Thomas Thrainer
    Examples::
264 1a732a74 Thomas Thrainer

265 1a732a74 Thomas Thrainer
      # Acquire all nodes and one instance
266 1a732a74 Thomas Thrainer
      self.needed_locks = {
267 1a732a74 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
268 1a732a74 Thomas Thrainer
        locking.LEVEL_INSTANCE: ['instance1.example.com'],
269 1a732a74 Thomas Thrainer
      }
270 1a732a74 Thomas Thrainer
      # Acquire just two nodes
271 1a732a74 Thomas Thrainer
      self.needed_locks = {
272 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: ['node1-uuid', 'node2-uuid'],
273 1a732a74 Thomas Thrainer
      }
274 1a732a74 Thomas Thrainer
      # Acquire no locks
275 1a732a74 Thomas Thrainer
      self.needed_locks = {} # No, you can't leave it to the default value None
276 1a732a74 Thomas Thrainer

277 1a732a74 Thomas Thrainer
    """
278 1a732a74 Thomas Thrainer
    # The implementation of this method is mandatory only if the new LU is
279 1a732a74 Thomas Thrainer
    # concurrent, so that old LUs don't need to be changed all at the same
280 1a732a74 Thomas Thrainer
    # time.
281 1a732a74 Thomas Thrainer
    if self.REQ_BGL:
282 1a732a74 Thomas Thrainer
      self.needed_locks = {} # Exclusive LUs don't need locks.
283 1a732a74 Thomas Thrainer
    else:
284 1a732a74 Thomas Thrainer
      raise NotImplementedError
285 1a732a74 Thomas Thrainer
286 1a732a74 Thomas Thrainer
  def DeclareLocks(self, level):
287 1a732a74 Thomas Thrainer
    """Declare LU locking needs for a level
288 1a732a74 Thomas Thrainer

289 1a732a74 Thomas Thrainer
    While most LUs can just declare their locking needs at ExpandNames time,
290 1a732a74 Thomas Thrainer
    sometimes there's the need to calculate some locks after having acquired
291 1a732a74 Thomas Thrainer
    the ones before. This function is called just before acquiring locks at a
292 1a732a74 Thomas Thrainer
    particular level, but after acquiring the ones at lower levels, and permits
293 1a732a74 Thomas Thrainer
    such calculations. It can be used to modify self.needed_locks, and by
294 1a732a74 Thomas Thrainer
    default it does nothing.
295 1a732a74 Thomas Thrainer

296 1a732a74 Thomas Thrainer
    This function is only called if you have something already set in
297 1a732a74 Thomas Thrainer
    self.needed_locks for the level.
298 1a732a74 Thomas Thrainer

299 1a732a74 Thomas Thrainer
    @param level: Locking level which is going to be locked
300 1a732a74 Thomas Thrainer
    @type level: member of L{ganeti.locking.LEVELS}
301 1a732a74 Thomas Thrainer

302 1a732a74 Thomas Thrainer
    """
303 1a732a74 Thomas Thrainer
304 1a732a74 Thomas Thrainer
  def CheckPrereq(self):
305 1a732a74 Thomas Thrainer
    """Check prerequisites for this LU.
306 1a732a74 Thomas Thrainer

307 1a732a74 Thomas Thrainer
    This method should check that the prerequisites for the execution
308 1a732a74 Thomas Thrainer
    of this LU are fulfilled. It can do internode communication, but
309 1a732a74 Thomas Thrainer
    it should be idempotent - no cluster or system changes are
310 1a732a74 Thomas Thrainer
    allowed.
311 1a732a74 Thomas Thrainer

312 1a732a74 Thomas Thrainer
    The method should raise errors.OpPrereqError in case something is
313 1a732a74 Thomas Thrainer
    not fulfilled. Its return value is ignored.
314 1a732a74 Thomas Thrainer

315 1a732a74 Thomas Thrainer
    This method should also update all the parameters of the opcode to
316 1a732a74 Thomas Thrainer
    their canonical form if it hasn't been done by ExpandNames before.
317 1a732a74 Thomas Thrainer

318 1a732a74 Thomas Thrainer
    """
319 1a732a74 Thomas Thrainer
    if self.tasklets is not None:
320 1a732a74 Thomas Thrainer
      for (idx, tl) in enumerate(self.tasklets):
321 1a732a74 Thomas Thrainer
        logging.debug("Checking prerequisites for tasklet %s/%s",
322 1a732a74 Thomas Thrainer
                      idx + 1, len(self.tasklets))
323 1a732a74 Thomas Thrainer
        tl.CheckPrereq()
324 1a732a74 Thomas Thrainer
    else:
325 1a732a74 Thomas Thrainer
      pass
326 1a732a74 Thomas Thrainer
327 1a732a74 Thomas Thrainer
  def Exec(self, feedback_fn):
328 1a732a74 Thomas Thrainer
    """Execute the LU.
329 1a732a74 Thomas Thrainer

330 1a732a74 Thomas Thrainer
    This method should implement the actual work. It should raise
331 1a732a74 Thomas Thrainer
    errors.OpExecError for failures that are somewhat dealt with in
332 1a732a74 Thomas Thrainer
    code, or expected.
333 1a732a74 Thomas Thrainer

334 1a732a74 Thomas Thrainer
    """
335 1a732a74 Thomas Thrainer
    if self.tasklets is not None:
336 1a732a74 Thomas Thrainer
      for (idx, tl) in enumerate(self.tasklets):
337 1a732a74 Thomas Thrainer
        logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
338 1a732a74 Thomas Thrainer
        tl.Exec(feedback_fn)
339 1a732a74 Thomas Thrainer
    else:
340 1a732a74 Thomas Thrainer
      raise NotImplementedError
341 1a732a74 Thomas Thrainer
342 1a732a74 Thomas Thrainer
  def BuildHooksEnv(self):
343 1a732a74 Thomas Thrainer
    """Build hooks environment for this LU.
344 1a732a74 Thomas Thrainer

345 1a732a74 Thomas Thrainer
    @rtype: dict
346 1a732a74 Thomas Thrainer
    @return: Dictionary containing the environment that will be used for
347 1a732a74 Thomas Thrainer
      running the hooks for this LU. The keys of the dict must not be prefixed
348 1a732a74 Thomas Thrainer
      with "GANETI_"--that'll be added by the hooks runner. The hooks runner
349 1a732a74 Thomas Thrainer
      will extend the environment with additional variables. If no environment
350 1a732a74 Thomas Thrainer
      should be defined, an empty dictionary should be returned (not C{None}).
351 1a732a74 Thomas Thrainer
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
352 1a732a74 Thomas Thrainer
      will not be called.
353 1a732a74 Thomas Thrainer

354 1a732a74 Thomas Thrainer
    """
355 1a732a74 Thomas Thrainer
    raise NotImplementedError
356 1a732a74 Thomas Thrainer
357 1a732a74 Thomas Thrainer
  def BuildHooksNodes(self):
358 1a732a74 Thomas Thrainer
    """Build list of nodes to run LU's hooks.
359 1a732a74 Thomas Thrainer

360 ff1c051b Thomas Thrainer
    @rtype: tuple; (list, list)
361 1c3231aa Thomas Thrainer
    @return: Tuple containing a list of node UUIDs on which the hook
362 1c3231aa Thomas Thrainer
      should run before the execution and a list of node UUIDs on which the
363 ff1c051b Thomas Thrainer
      hook should run after the execution.
364 1c3231aa Thomas Thrainer
      No nodes should be returned as an empty list (and not None).
365 1a732a74 Thomas Thrainer
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
366 1a732a74 Thomas Thrainer
      will not be called.
367 1a732a74 Thomas Thrainer

368 1a732a74 Thomas Thrainer
    """
369 1a732a74 Thomas Thrainer
    raise NotImplementedError
370 1a732a74 Thomas Thrainer
371 237a833c Thomas Thrainer
  def PreparePostHookNodes(self, post_hook_node_uuids):
372 237a833c Thomas Thrainer
    """Extend list of nodes to run the post LU hook.
373 237a833c Thomas Thrainer

374 237a833c Thomas Thrainer
    This method allows LUs to change the list of node UUIDs on which the
375 237a833c Thomas Thrainer
    post hook should run after the LU has been executed but before the post
376 237a833c Thomas Thrainer
    hook is run.
377 237a833c Thomas Thrainer

378 237a833c Thomas Thrainer
    @type post_hook_node_uuids: list
379 237a833c Thomas Thrainer
    @param post_hook_node_uuids: The initial list of node UUIDs to run the
380 237a833c Thomas Thrainer
      post hook on, as returned by L{BuildHooksNodes}.
381 237a833c Thomas Thrainer
    @rtype: list
382 237a833c Thomas Thrainer
    @return: list of node UUIDs on which the post hook should run. The default
383 237a833c Thomas Thrainer
      implementation returns the passed in C{post_hook_node_uuids}, but
384 237a833c Thomas Thrainer
      custom implementations can choose to alter the list.
385 237a833c Thomas Thrainer

386 237a833c Thomas Thrainer
    """
387 237a833c Thomas Thrainer
    # For consistency with HooksCallBack we ignore the "could be a function"
388 237a833c Thomas Thrainer
    # warning
389 237a833c Thomas Thrainer
    # pylint: disable=R0201
390 237a833c Thomas Thrainer
    return post_hook_node_uuids
391 237a833c Thomas Thrainer
392 1a732a74 Thomas Thrainer
  def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
393 1a732a74 Thomas Thrainer
    """Notify the LU about the results of its hooks.
394 1a732a74 Thomas Thrainer

395 1a732a74 Thomas Thrainer
    This method is called every time a hooks phase is executed, and notifies
396 1a732a74 Thomas Thrainer
    the Logical Unit about the hooks' result. The LU can then use it to alter
397 1a732a74 Thomas Thrainer
    its result based on the hooks.  By default the method does nothing and the
398 1a732a74 Thomas Thrainer
    previous result is passed back unchanged but any LU can define it if it
399 1a732a74 Thomas Thrainer
    wants to use the local cluster hook-scripts somehow.
400 1a732a74 Thomas Thrainer

401 1a732a74 Thomas Thrainer
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
402 1a732a74 Thomas Thrainer
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
403 1a732a74 Thomas Thrainer
    @param hook_results: the results of the multi-node hooks rpc call
404 1a732a74 Thomas Thrainer
    @param feedback_fn: function used send feedback back to the caller
405 1a732a74 Thomas Thrainer
    @param lu_result: the previous Exec result this LU had, or None
406 1a732a74 Thomas Thrainer
        in the PRE phase
407 1a732a74 Thomas Thrainer
    @return: the new Exec result, based on the previous result
408 1a732a74 Thomas Thrainer
        and hook results
409 1a732a74 Thomas Thrainer

410 1a732a74 Thomas Thrainer
    """
411 1a732a74 Thomas Thrainer
    # API must be kept, thus we ignore the unused argument and could
412 1a732a74 Thomas Thrainer
    # be a function warnings
413 1a732a74 Thomas Thrainer
    # pylint: disable=W0613,R0201
414 1a732a74 Thomas Thrainer
    return lu_result
415 1a732a74 Thomas Thrainer
416 1a732a74 Thomas Thrainer
  def _ExpandAndLockInstance(self):
417 1a732a74 Thomas Thrainer
    """Helper function to expand and lock an instance.
418 1a732a74 Thomas Thrainer

419 1a732a74 Thomas Thrainer
    Many LUs that work on an instance take its name in self.op.instance_name
420 1a732a74 Thomas Thrainer
    and need to expand it and then declare the expanded name for locking. This
421 1a732a74 Thomas Thrainer
    function does it, and then updates self.op.instance_name to the expanded
422 1a732a74 Thomas Thrainer
    name. It also initializes needed_locks as a dict, if this hasn't been done
423 1a732a74 Thomas Thrainer
    before.
424 1a732a74 Thomas Thrainer

425 1a732a74 Thomas Thrainer
    """
426 1a732a74 Thomas Thrainer
    if self.needed_locks is None:
427 1a732a74 Thomas Thrainer
      self.needed_locks = {}
428 1a732a74 Thomas Thrainer
    else:
429 1a732a74 Thomas Thrainer
      assert locking.LEVEL_INSTANCE not in self.needed_locks, \
430 1a732a74 Thomas Thrainer
        "_ExpandAndLockInstance called with instance-level locks set"
431 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
432 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
433 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
434 1a732a74 Thomas Thrainer
    self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
435 1a732a74 Thomas Thrainer
436 1a732a74 Thomas Thrainer
  def _LockInstancesNodes(self, primary_only=False,
437 1a732a74 Thomas Thrainer
                          level=locking.LEVEL_NODE):
438 1a732a74 Thomas Thrainer
    """Helper function to declare instances' nodes for locking.
439 1a732a74 Thomas Thrainer

440 1a732a74 Thomas Thrainer
    This function should be called after locking one or more instances to lock
441 1a732a74 Thomas Thrainer
    their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
442 1a732a74 Thomas Thrainer
    with all primary or secondary nodes for instances already locked and
443 1a732a74 Thomas Thrainer
    present in self.needed_locks[locking.LEVEL_INSTANCE].
444 1a732a74 Thomas Thrainer

445 1a732a74 Thomas Thrainer
    It should be called from DeclareLocks, and for safety only works if
446 1a732a74 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] is set.
447 1a732a74 Thomas Thrainer

448 1a732a74 Thomas Thrainer
    In the future it may grow parameters to just lock some instance's nodes, or
449 1a732a74 Thomas Thrainer
    to just lock primaries or secondary nodes, if needed.
450 1a732a74 Thomas Thrainer

451 1a732a74 Thomas Thrainer
    If should be called in DeclareLocks in a way similar to::
452 1a732a74 Thomas Thrainer

453 1a732a74 Thomas Thrainer
      if level == locking.LEVEL_NODE:
454 1a732a74 Thomas Thrainer
        self._LockInstancesNodes()
455 1a732a74 Thomas Thrainer

456 1a732a74 Thomas Thrainer
    @type primary_only: boolean
457 1a732a74 Thomas Thrainer
    @param primary_only: only lock primary nodes of locked instances
458 1a732a74 Thomas Thrainer
    @param level: Which lock level to use for locking nodes
459 1a732a74 Thomas Thrainer

460 1a732a74 Thomas Thrainer
    """
461 1a732a74 Thomas Thrainer
    assert level in self.recalculate_locks, \
462 1a732a74 Thomas Thrainer
      "_LockInstancesNodes helper function called with no nodes to recalculate"
463 1a732a74 Thomas Thrainer
464 1a732a74 Thomas Thrainer
    # TODO: check if we're really been called with the instance locks held
465 1a732a74 Thomas Thrainer
466 1a732a74 Thomas Thrainer
    # For now we'll replace self.needed_locks[locking.LEVEL_NODE], but in the
467 1a732a74 Thomas Thrainer
    # future we might want to have different behaviors depending on the value
468 1a732a74 Thomas Thrainer
    # of self.recalculate_locks[locking.LEVEL_NODE]
469 1c3231aa Thomas Thrainer
    wanted_node_uuids = []
470 1a732a74 Thomas Thrainer
    locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
471 da4a52a3 Thomas Thrainer
    for _, instance in self.cfg.GetMultiInstanceInfoByName(locked_i):
472 1c3231aa Thomas Thrainer
      wanted_node_uuids.append(instance.primary_node)
473 1a732a74 Thomas Thrainer
      if not primary_only:
474 1c3231aa Thomas Thrainer
        wanted_node_uuids.extend(instance.secondary_nodes)
475 1a732a74 Thomas Thrainer
476 1a732a74 Thomas Thrainer
    if self.recalculate_locks[level] == constants.LOCKS_REPLACE:
477 1c3231aa Thomas Thrainer
      self.needed_locks[level] = wanted_node_uuids
478 1a732a74 Thomas Thrainer
    elif self.recalculate_locks[level] == constants.LOCKS_APPEND:
479 1c3231aa Thomas Thrainer
      self.needed_locks[level].extend(wanted_node_uuids)
480 1a732a74 Thomas Thrainer
    else:
481 1a732a74 Thomas Thrainer
      raise errors.ProgrammerError("Unknown recalculation mode")
482 1a732a74 Thomas Thrainer
483 1a732a74 Thomas Thrainer
    del self.recalculate_locks[level]
484 1a732a74 Thomas Thrainer
485 1a732a74 Thomas Thrainer
486 1a732a74 Thomas Thrainer
class NoHooksLU(LogicalUnit): # pylint: disable=W0223
487 1a732a74 Thomas Thrainer
  """Simple LU which runs no hooks.
488 1a732a74 Thomas Thrainer

489 1a732a74 Thomas Thrainer
  This LU is intended as a parent for other LogicalUnits which will
490 1a732a74 Thomas Thrainer
  run no hooks, in order to reduce duplicate code.
491 1a732a74 Thomas Thrainer

492 1a732a74 Thomas Thrainer
  """
493 1a732a74 Thomas Thrainer
  HPATH = None
494 1a732a74 Thomas Thrainer
  HTYPE = None
495 1a732a74 Thomas Thrainer
496 1a732a74 Thomas Thrainer
  def BuildHooksEnv(self):
497 1a732a74 Thomas Thrainer
    """Empty BuildHooksEnv for NoHooksLu.
498 1a732a74 Thomas Thrainer

499 1a732a74 Thomas Thrainer
    This just raises an error.
500 1a732a74 Thomas Thrainer

501 1a732a74 Thomas Thrainer
    """
502 1a732a74 Thomas Thrainer
    raise AssertionError("BuildHooksEnv called for NoHooksLUs")
503 1a732a74 Thomas Thrainer
504 1a732a74 Thomas Thrainer
  def BuildHooksNodes(self):
505 1a732a74 Thomas Thrainer
    """Empty BuildHooksNodes for NoHooksLU.
506 1a732a74 Thomas Thrainer

507 1a732a74 Thomas Thrainer
    """
508 1a732a74 Thomas Thrainer
    raise AssertionError("BuildHooksNodes called for NoHooksLU")
509 1a732a74 Thomas Thrainer
510 237a833c Thomas Thrainer
  def PreparePostHookNodes(self, post_hook_node_uuids):
511 237a833c Thomas Thrainer
    """Empty PreparePostHookNodes for NoHooksLU.
512 237a833c Thomas Thrainer

513 237a833c Thomas Thrainer
    """
514 237a833c Thomas Thrainer
    raise AssertionError("PreparePostHookNodes called for NoHooksLU")
515 237a833c Thomas Thrainer
516 1a732a74 Thomas Thrainer
517 1a732a74 Thomas Thrainer
class Tasklet:
518 1a732a74 Thomas Thrainer
  """Tasklet base class.
519 1a732a74 Thomas Thrainer

520 1a732a74 Thomas Thrainer
  Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
521 1a732a74 Thomas Thrainer
  they can mix legacy code with tasklets. Locking needs to be done in the LU,
522 1a732a74 Thomas Thrainer
  tasklets know nothing about locks.
523 1a732a74 Thomas Thrainer

524 1a732a74 Thomas Thrainer
  Subclasses must follow these rules:
525 1a732a74 Thomas Thrainer
    - Implement CheckPrereq
526 1a732a74 Thomas Thrainer
    - Implement Exec
527 1a732a74 Thomas Thrainer

528 1a732a74 Thomas Thrainer
  """
529 1a732a74 Thomas Thrainer
  def __init__(self, lu):
530 1a732a74 Thomas Thrainer
    self.lu = lu
531 1a732a74 Thomas Thrainer
532 1a732a74 Thomas Thrainer
    # Shortcuts
533 1a732a74 Thomas Thrainer
    self.cfg = lu.cfg
534 1a732a74 Thomas Thrainer
    self.rpc = lu.rpc
535 1a732a74 Thomas Thrainer
536 1a732a74 Thomas Thrainer
  def CheckPrereq(self):
537 1a732a74 Thomas Thrainer
    """Check prerequisites for this tasklets.
538 1a732a74 Thomas Thrainer

539 1a732a74 Thomas Thrainer
    This method should check whether the prerequisites for the execution of
540 1a732a74 Thomas Thrainer
    this tasklet are fulfilled. It can do internode communication, but it
541 1a732a74 Thomas Thrainer
    should be idempotent - no cluster or system changes are allowed.
542 1a732a74 Thomas Thrainer

543 1a732a74 Thomas Thrainer
    The method should raise errors.OpPrereqError in case something is not
544 1a732a74 Thomas Thrainer
    fulfilled. Its return value is ignored.
545 1a732a74 Thomas Thrainer

546 1a732a74 Thomas Thrainer
    This method should also update all parameters to their canonical form if it
547 1a732a74 Thomas Thrainer
    hasn't been done before.
548 1a732a74 Thomas Thrainer

549 1a732a74 Thomas Thrainer
    """
550 1a732a74 Thomas Thrainer
    pass
551 1a732a74 Thomas Thrainer
552 1a732a74 Thomas Thrainer
  def Exec(self, feedback_fn):
553 1a732a74 Thomas Thrainer
    """Execute the tasklet.
554 1a732a74 Thomas Thrainer

555 1a732a74 Thomas Thrainer
    This method should implement the actual work. It should raise
556 1a732a74 Thomas Thrainer
    errors.OpExecError for failures that are somewhat dealt with in code, or
557 1a732a74 Thomas Thrainer
    expected.
558 1a732a74 Thomas Thrainer

559 1a732a74 Thomas Thrainer
    """
560 1a732a74 Thomas Thrainer
    raise NotImplementedError
561 1a732a74 Thomas Thrainer
562 1a732a74 Thomas Thrainer
563 5eacbcae Thomas Thrainer
class QueryBase:
564 1a732a74 Thomas Thrainer
  """Base for query utility classes.
565 1a732a74 Thomas Thrainer

566 1a732a74 Thomas Thrainer
  """
567 1a732a74 Thomas Thrainer
  #: Attribute holding field definitions
568 1a732a74 Thomas Thrainer
  FIELDS = None
569 1a732a74 Thomas Thrainer
570 1a732a74 Thomas Thrainer
  #: Field to sort by
571 1a732a74 Thomas Thrainer
  SORT_FIELD = "name"
572 1a732a74 Thomas Thrainer
573 1a732a74 Thomas Thrainer
  def __init__(self, qfilter, fields, use_locking):
574 1a732a74 Thomas Thrainer
    """Initializes this class.
575 1a732a74 Thomas Thrainer

576 1a732a74 Thomas Thrainer
    """
577 1a732a74 Thomas Thrainer
    self.use_locking = use_locking
578 1a732a74 Thomas Thrainer
579 1a732a74 Thomas Thrainer
    self.query = query.Query(self.FIELDS, fields, qfilter=qfilter,
580 1a732a74 Thomas Thrainer
                             namefield=self.SORT_FIELD)
581 1a732a74 Thomas Thrainer
    self.requested_data = self.query.RequestedData()
582 1a732a74 Thomas Thrainer
    self.names = self.query.RequestedNames()
583 1a732a74 Thomas Thrainer
584 1a732a74 Thomas Thrainer
    # Sort only if no names were requested
585 1a732a74 Thomas Thrainer
    self.sort_by_name = not self.names
586 1a732a74 Thomas Thrainer
587 1a732a74 Thomas Thrainer
    self.do_locking = None
588 1a732a74 Thomas Thrainer
    self.wanted = None
589 1a732a74 Thomas Thrainer
590 1a732a74 Thomas Thrainer
  def _GetNames(self, lu, all_names, lock_level):
591 1a732a74 Thomas Thrainer
    """Helper function to determine names asked for in the query.
592 1a732a74 Thomas Thrainer

593 1a732a74 Thomas Thrainer
    """
594 1a732a74 Thomas Thrainer
    if self.do_locking:
595 1a732a74 Thomas Thrainer
      names = lu.owned_locks(lock_level)
596 1a732a74 Thomas Thrainer
    else:
597 1a732a74 Thomas Thrainer
      names = all_names
598 1a732a74 Thomas Thrainer
599 1a732a74 Thomas Thrainer
    if self.wanted == locking.ALL_SET:
600 1a732a74 Thomas Thrainer
      assert not self.names
601 1a732a74 Thomas Thrainer
      # caller didn't specify names, so ordering is not important
602 1a732a74 Thomas Thrainer
      return utils.NiceSort(names)
603 1a732a74 Thomas Thrainer
604 1a732a74 Thomas Thrainer
    # caller specified names and we must keep the same order
605 1a732a74 Thomas Thrainer
    assert self.names
606 1a732a74 Thomas Thrainer
607 1a732a74 Thomas Thrainer
    missing = set(self.wanted).difference(names)
608 1a732a74 Thomas Thrainer
    if missing:
609 1a732a74 Thomas Thrainer
      raise errors.OpExecError("Some items were removed before retrieving"
610 1a732a74 Thomas Thrainer
                               " their data: %s" % missing)
611 1a732a74 Thomas Thrainer
612 1a732a74 Thomas Thrainer
    # Return expanded names
613 1a732a74 Thomas Thrainer
    return self.wanted
614 1a732a74 Thomas Thrainer
615 1a732a74 Thomas Thrainer
  def ExpandNames(self, lu):
616 1a732a74 Thomas Thrainer
    """Expand names for this query.
617 1a732a74 Thomas Thrainer

618 1a732a74 Thomas Thrainer
    See L{LogicalUnit.ExpandNames}.
619 1a732a74 Thomas Thrainer

620 1a732a74 Thomas Thrainer
    """
621 1a732a74 Thomas Thrainer
    raise NotImplementedError()
622 1a732a74 Thomas Thrainer
623 1a732a74 Thomas Thrainer
  def DeclareLocks(self, lu, level):
624 1a732a74 Thomas Thrainer
    """Declare locks for this query.
625 1a732a74 Thomas Thrainer

626 1a732a74 Thomas Thrainer
    See L{LogicalUnit.DeclareLocks}.
627 1a732a74 Thomas Thrainer

628 1a732a74 Thomas Thrainer
    """
629 1a732a74 Thomas Thrainer
    raise NotImplementedError()
630 1a732a74 Thomas Thrainer
631 1a732a74 Thomas Thrainer
  def _GetQueryData(self, lu):
632 1a732a74 Thomas Thrainer
    """Collects all data for this query.
633 1a732a74 Thomas Thrainer

634 1a732a74 Thomas Thrainer
    @return: Query data object
635 1a732a74 Thomas Thrainer

636 1a732a74 Thomas Thrainer
    """
637 1a732a74 Thomas Thrainer
    raise NotImplementedError()
638 1a732a74 Thomas Thrainer
639 1a732a74 Thomas Thrainer
  def NewStyleQuery(self, lu):
640 1a732a74 Thomas Thrainer
    """Collect data and execute query.
641 1a732a74 Thomas Thrainer

642 1a732a74 Thomas Thrainer
    """
643 1a732a74 Thomas Thrainer
    return query.GetQueryResponse(self.query, self._GetQueryData(lu),
644 1a732a74 Thomas Thrainer
                                  sort_by_name=self.sort_by_name)
645 1a732a74 Thomas Thrainer
646 1a732a74 Thomas Thrainer
  def OldStyleQuery(self, lu):
647 1a732a74 Thomas Thrainer
    """Collect data and execute query.
648 1a732a74 Thomas Thrainer

649 1a732a74 Thomas Thrainer
    """
650 1a732a74 Thomas Thrainer
    return self.query.OldStyleQuery(self._GetQueryData(lu),
651 1a732a74 Thomas Thrainer
                                    sort_by_name=self.sort_by_name)