Revision 1a732a74 lib/cmdlib/__init__.py

b/lib/cmdlib/__init__.py
29 29
# C0302: since we have waaaay too many lines in this module
30 30

  
31 31
import os
32
import os.path
33 32
import time
34 33
import re
35 34
import logging
......
64 63
from ganeti import network
65 64
from ganeti.masterd import iallocator
66 65

  
66
from ganeti.cmdlib.base import ResultWithJobs, LogicalUnit, NoHooksLU, \
67
  Tasklet, _QueryBase
68
from ganeti.cmdlib.common import _ExpandInstanceName, _ExpandItemName, \
69
  _ExpandNodeName
70

  
67 71
import ganeti.masterd.instance # pylint: disable=W0611
68 72

  
69 73

  
......
78 82
  ]))
79 83

  
80 84

  
81
class ResultWithJobs:
82
  """Data container for LU results with jobs.
83

  
84
  Instances of this class returned from L{LogicalUnit.Exec} will be recognized
85
  by L{mcpu._ProcessResult}. The latter will then submit the jobs
86
  contained in the C{jobs} attribute and include the job IDs in the opcode
87
  result.
88

  
89
  """
90
  def __init__(self, jobs, **kwargs):
91
    """Initializes this class.
92

  
93
    Additional return values can be specified as keyword arguments.
94

  
95
    @type jobs: list of lists of L{opcode.OpCode}
96
    @param jobs: A list of lists of opcode objects
97

  
98
    """
99
    self.jobs = jobs
100
    self.other = kwargs
101

  
102

  
103
class LogicalUnit(object):
104
  """Logical Unit base class.
105

  
106
  Subclasses must follow these rules:
107
    - implement ExpandNames
108
    - implement CheckPrereq (except when tasklets are used)
109
    - implement Exec (except when tasklets are used)
110
    - implement BuildHooksEnv
111
    - implement BuildHooksNodes
112
    - redefine HPATH and HTYPE
113
    - optionally redefine their run requirements:
114
        REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
115

  
116
  Note that all commands require root permissions.
117

  
118
  @ivar dry_run_result: the value (if any) that will be returned to the caller
119
      in dry-run mode (signalled by opcode dry_run parameter)
120

  
121
  """
122
  HPATH = None
123
  HTYPE = None
124
  REQ_BGL = True
125

  
126
  def __init__(self, processor, op, context, rpc_runner):
127
    """Constructor for LogicalUnit.
128

  
129
    This needs to be overridden in derived classes in order to check op
130
    validity.
131

  
132
    """
133
    self.proc = processor
134
    self.op = op
135
    self.cfg = context.cfg
136
    self.glm = context.glm
137
    # readability alias
138
    self.owned_locks = context.glm.list_owned
139
    self.context = context
140
    self.rpc = rpc_runner
141

  
142
    # Dictionaries used to declare locking needs to mcpu
143
    self.needed_locks = None
144
    self.share_locks = dict.fromkeys(locking.LEVELS, 0)
145
    self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False)
146

  
147
    self.add_locks = {}
148
    self.remove_locks = {}
149

  
150
    # Used to force good behavior when calling helper functions
151
    self.recalculate_locks = {}
152

  
153
    # logging
154
    self.Log = processor.Log # pylint: disable=C0103
155
    self.LogWarning = processor.LogWarning # pylint: disable=C0103
156
    self.LogInfo = processor.LogInfo # pylint: disable=C0103
157
    self.LogStep = processor.LogStep # pylint: disable=C0103
158
    # support for dry-run
159
    self.dry_run_result = None
160
    # support for generic debug attribute
161
    if (not hasattr(self.op, "debug_level") or
162
        not isinstance(self.op.debug_level, int)):
163
      self.op.debug_level = 0
164

  
165
    # Tasklets
166
    self.tasklets = None
167

  
168
    # Validate opcode parameters and set defaults
169
    self.op.Validate(True)
170

  
171
    self.CheckArguments()
172

  
173
  def CheckArguments(self):
174
    """Check syntactic validity for the opcode arguments.
175

  
176
    This method is for doing a simple syntactic check and ensure
177
    validity of opcode parameters, without any cluster-related
178
    checks. While the same can be accomplished in ExpandNames and/or
179
    CheckPrereq, doing these separate is better because:
180

  
181
      - ExpandNames is left as as purely a lock-related function
182
      - CheckPrereq is run after we have acquired locks (and possible
183
        waited for them)
184

  
185
    The function is allowed to change the self.op attribute so that
186
    later methods can no longer worry about missing parameters.
187

  
188
    """
189
    pass
190

  
191
  def ExpandNames(self):
192
    """Expand names for this LU.
193

  
194
    This method is called before starting to execute the opcode, and it should
195
    update all the parameters of the opcode to their canonical form (e.g. a
196
    short node name must be fully expanded after this method has successfully
197
    completed). This way locking, hooks, logging, etc. can work correctly.
198

  
199
    LUs which implement this method must also populate the self.needed_locks
200
    member, as a dict with lock levels as keys, and a list of needed lock names
201
    as values. Rules:
202

  
203
      - use an empty dict if you don't need any lock
204
      - if you don't need any lock at a particular level omit that
205
        level (note that in this case C{DeclareLocks} won't be called
206
        at all for that level)
207
      - if you need locks at a level, but you can't calculate it in
208
        this function, initialise that level with an empty list and do
209
        further processing in L{LogicalUnit.DeclareLocks} (see that
210
        function's docstring)
211
      - don't put anything for the BGL level
212
      - if you want all locks at a level use L{locking.ALL_SET} as a value
213

  
214
    If you need to share locks (rather than acquire them exclusively) at one
215
    level you can modify self.share_locks, setting a true value (usually 1) for
216
    that level. By default locks are not shared.
217

  
218
    This function can also define a list of tasklets, which then will be
219
    executed in order instead of the usual LU-level CheckPrereq and Exec
220
    functions, if those are not defined by the LU.
221

  
222
    Examples::
223

  
224
      # Acquire all nodes and one instance
225
      self.needed_locks = {
226
        locking.LEVEL_NODE: locking.ALL_SET,
227
        locking.LEVEL_INSTANCE: ['instance1.example.com'],
228
      }
229
      # Acquire just two nodes
230
      self.needed_locks = {
231
        locking.LEVEL_NODE: ['node1.example.com', 'node2.example.com'],
232
      }
233
      # Acquire no locks
234
      self.needed_locks = {} # No, you can't leave it to the default value None
235

  
236
    """
237
    # The implementation of this method is mandatory only if the new LU is
238
    # concurrent, so that old LUs don't need to be changed all at the same
239
    # time.
240
    if self.REQ_BGL:
241
      self.needed_locks = {} # Exclusive LUs don't need locks.
242
    else:
243
      raise NotImplementedError
244

  
245
  def DeclareLocks(self, level):
246
    """Declare LU locking needs for a level
247

  
248
    While most LUs can just declare their locking needs at ExpandNames time,
249
    sometimes there's the need to calculate some locks after having acquired
250
    the ones before. This function is called just before acquiring locks at a
251
    particular level, but after acquiring the ones at lower levels, and permits
252
    such calculations. It can be used to modify self.needed_locks, and by
253
    default it does nothing.
254

  
255
    This function is only called if you have something already set in
256
    self.needed_locks for the level.
257

  
258
    @param level: Locking level which is going to be locked
259
    @type level: member of L{ganeti.locking.LEVELS}
260

  
261
    """
262

  
263
  def CheckPrereq(self):
264
    """Check prerequisites for this LU.
265

  
266
    This method should check that the prerequisites for the execution
267
    of this LU are fulfilled. It can do internode communication, but
268
    it should be idempotent - no cluster or system changes are
269
    allowed.
270

  
271
    The method should raise errors.OpPrereqError in case something is
272
    not fulfilled. Its return value is ignored.
273

  
274
    This method should also update all the parameters of the opcode to
275
    their canonical form if it hasn't been done by ExpandNames before.
276

  
277
    """
278
    if self.tasklets is not None:
279
      for (idx, tl) in enumerate(self.tasklets):
280
        logging.debug("Checking prerequisites for tasklet %s/%s",
281
                      idx + 1, len(self.tasklets))
282
        tl.CheckPrereq()
283
    else:
284
      pass
285

  
286
  def Exec(self, feedback_fn):
287
    """Execute the LU.
288

  
289
    This method should implement the actual work. It should raise
290
    errors.OpExecError for failures that are somewhat dealt with in
291
    code, or expected.
292

  
293
    """
294
    if self.tasklets is not None:
295
      for (idx, tl) in enumerate(self.tasklets):
296
        logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
297
        tl.Exec(feedback_fn)
298
    else:
299
      raise NotImplementedError
300

  
301
  def BuildHooksEnv(self):
302
    """Build hooks environment for this LU.
303

  
304
    @rtype: dict
305
    @return: Dictionary containing the environment that will be used for
306
      running the hooks for this LU. The keys of the dict must not be prefixed
307
      with "GANETI_"--that'll be added by the hooks runner. The hooks runner
308
      will extend the environment with additional variables. If no environment
309
      should be defined, an empty dictionary should be returned (not C{None}).
310
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
311
      will not be called.
312

  
313
    """
314
    raise NotImplementedError
315

  
316
  def BuildHooksNodes(self):
317
    """Build list of nodes to run LU's hooks.
318

  
319
    @rtype: tuple; (list, list)
320
    @return: Tuple containing a list of node names on which the hook
321
      should run before the execution and a list of node names on which the
322
      hook should run after the execution. No nodes should be returned as an
323
      empty list (and not None).
324
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
325
      will not be called.
326

  
327
    """
328
    raise NotImplementedError
329

  
330
  def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
331
    """Notify the LU about the results of its hooks.
332

  
333
    This method is called every time a hooks phase is executed, and notifies
334
    the Logical Unit about the hooks' result. The LU can then use it to alter
335
    its result based on the hooks.  By default the method does nothing and the
336
    previous result is passed back unchanged but any LU can define it if it
337
    wants to use the local cluster hook-scripts somehow.
338

  
339
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
340
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
341
    @param hook_results: the results of the multi-node hooks rpc call
342
    @param feedback_fn: function used send feedback back to the caller
343
    @param lu_result: the previous Exec result this LU had, or None
344
        in the PRE phase
345
    @return: the new Exec result, based on the previous result
346
        and hook results
347

  
348
    """
349
    # API must be kept, thus we ignore the unused argument and could
350
    # be a function warnings
351
    # pylint: disable=W0613,R0201
352
    return lu_result
353

  
354
  def _ExpandAndLockInstance(self):
355
    """Helper function to expand and lock an instance.
356

  
357
    Many LUs that work on an instance take its name in self.op.instance_name
358
    and need to expand it and then declare the expanded name for locking. This
359
    function does it, and then updates self.op.instance_name to the expanded
360
    name. It also initializes needed_locks as a dict, if this hasn't been done
361
    before.
362

  
363
    """
364
    if self.needed_locks is None:
365
      self.needed_locks = {}
366
    else:
367
      assert locking.LEVEL_INSTANCE not in self.needed_locks, \
368
        "_ExpandAndLockInstance called with instance-level locks set"
369
    self.op.instance_name = _ExpandInstanceName(self.cfg,
370
                                                self.op.instance_name)
371
    self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
372

  
373
  def _LockInstancesNodes(self, primary_only=False,
374
                          level=locking.LEVEL_NODE):
375
    """Helper function to declare instances' nodes for locking.
376

  
377
    This function should be called after locking one or more instances to lock
378
    their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
379
    with all primary or secondary nodes for instances already locked and
380
    present in self.needed_locks[locking.LEVEL_INSTANCE].
381

  
382
    It should be called from DeclareLocks, and for safety only works if
383
    self.recalculate_locks[locking.LEVEL_NODE] is set.
384

  
385
    In the future it may grow parameters to just lock some instance's nodes, or
386
    to just lock primaries or secondary nodes, if needed.
387

  
388
    If should be called in DeclareLocks in a way similar to::
389

  
390
      if level == locking.LEVEL_NODE:
391
        self._LockInstancesNodes()
392

  
393
    @type primary_only: boolean
394
    @param primary_only: only lock primary nodes of locked instances
395
    @param level: Which lock level to use for locking nodes
396

  
397
    """
398
    assert level in self.recalculate_locks, \
399
      "_LockInstancesNodes helper function called with no nodes to recalculate"
400

  
401
    # TODO: check if we're really been called with the instance locks held
402

  
403
    # For now we'll replace self.needed_locks[locking.LEVEL_NODE], but in the
404
    # future we might want to have different behaviors depending on the value
405
    # of self.recalculate_locks[locking.LEVEL_NODE]
406
    wanted_nodes = []
407
    locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
408
    for _, instance in self.cfg.GetMultiInstanceInfo(locked_i):
409
      wanted_nodes.append(instance.primary_node)
410
      if not primary_only:
411
        wanted_nodes.extend(instance.secondary_nodes)
412

  
413
    if self.recalculate_locks[level] == constants.LOCKS_REPLACE:
414
      self.needed_locks[level] = wanted_nodes
415
    elif self.recalculate_locks[level] == constants.LOCKS_APPEND:
416
      self.needed_locks[level].extend(wanted_nodes)
417
    else:
418
      raise errors.ProgrammerError("Unknown recalculation mode")
419

  
420
    del self.recalculate_locks[level]
421

  
422

  
423
class NoHooksLU(LogicalUnit): # pylint: disable=W0223
424
  """Simple LU which runs no hooks.
425

  
426
  This LU is intended as a parent for other LogicalUnits which will
427
  run no hooks, in order to reduce duplicate code.
428

  
429
  """
430
  HPATH = None
431
  HTYPE = None
432

  
433
  def BuildHooksEnv(self):
434
    """Empty BuildHooksEnv for NoHooksLu.
435

  
436
    This just raises an error.
437

  
438
    """
439
    raise AssertionError("BuildHooksEnv called for NoHooksLUs")
440

  
441
  def BuildHooksNodes(self):
442
    """Empty BuildHooksNodes for NoHooksLU.
443

  
444
    """
445
    raise AssertionError("BuildHooksNodes called for NoHooksLU")
446

  
447

  
448
class Tasklet:
449
  """Tasklet base class.
450

  
451
  Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
452
  they can mix legacy code with tasklets. Locking needs to be done in the LU,
453
  tasklets know nothing about locks.
454

  
455
  Subclasses must follow these rules:
456
    - Implement CheckPrereq
457
    - Implement Exec
458

  
459
  """
460
  def __init__(self, lu):
461
    self.lu = lu
462

  
463
    # Shortcuts
464
    self.cfg = lu.cfg
465
    self.rpc = lu.rpc
466

  
467
  def CheckPrereq(self):
468
    """Check prerequisites for this tasklets.
469

  
470
    This method should check whether the prerequisites for the execution of
471
    this tasklet are fulfilled. It can do internode communication, but it
472
    should be idempotent - no cluster or system changes are allowed.
473

  
474
    The method should raise errors.OpPrereqError in case something is not
475
    fulfilled. Its return value is ignored.
476

  
477
    This method should also update all parameters to their canonical form if it
478
    hasn't been done before.
479

  
480
    """
481
    pass
482

  
483
  def Exec(self, feedback_fn):
484
    """Execute the tasklet.
485

  
486
    This method should implement the actual work. It should raise
487
    errors.OpExecError for failures that are somewhat dealt with in code, or
488
    expected.
489

  
490
    """
491
    raise NotImplementedError
492

  
493

  
494
class _QueryBase:
495
  """Base for query utility classes.
496

  
497
  """
498
  #: Attribute holding field definitions
499
  FIELDS = None
500

  
501
  #: Field to sort by
502
  SORT_FIELD = "name"
503

  
504
  def __init__(self, qfilter, fields, use_locking):
505
    """Initializes this class.
506

  
507
    """
508
    self.use_locking = use_locking
509

  
510
    self.query = query.Query(self.FIELDS, fields, qfilter=qfilter,
511
                             namefield=self.SORT_FIELD)
512
    self.requested_data = self.query.RequestedData()
513
    self.names = self.query.RequestedNames()
514

  
515
    # Sort only if no names were requested
516
    self.sort_by_name = not self.names
517

  
518
    self.do_locking = None
519
    self.wanted = None
520

  
521
  def _GetNames(self, lu, all_names, lock_level):
522
    """Helper function to determine names asked for in the query.
523

  
524
    """
525
    if self.do_locking:
526
      names = lu.owned_locks(lock_level)
527
    else:
528
      names = all_names
529

  
530
    if self.wanted == locking.ALL_SET:
531
      assert not self.names
532
      # caller didn't specify names, so ordering is not important
533
      return utils.NiceSort(names)
534

  
535
    # caller specified names and we must keep the same order
536
    assert self.names
537
    assert not self.do_locking or lu.glm.is_owned(lock_level)
538

  
539
    missing = set(self.wanted).difference(names)
540
    if missing:
541
      raise errors.OpExecError("Some items were removed before retrieving"
542
                               " their data: %s" % missing)
543

  
544
    # Return expanded names
545
    return self.wanted
546

  
547
  def ExpandNames(self, lu):
548
    """Expand names for this query.
549

  
550
    See L{LogicalUnit.ExpandNames}.
551

  
552
    """
553
    raise NotImplementedError()
554

  
555
  def DeclareLocks(self, lu, level):
556
    """Declare locks for this query.
557

  
558
    See L{LogicalUnit.DeclareLocks}.
559

  
560
    """
561
    raise NotImplementedError()
562

  
563
  def _GetQueryData(self, lu):
564
    """Collects all data for this query.
565

  
566
    @return: Query data object
567

  
568
    """
569
    raise NotImplementedError()
570

  
571
  def NewStyleQuery(self, lu):
572
    """Collect data and execute query.
573

  
574
    """
575
    return query.GetQueryResponse(self.query, self._GetQueryData(lu),
576
                                  sort_by_name=self.sort_by_name)
577

  
578
  def OldStyleQuery(self, lu):
579
    """Collect data and execute query.
580

  
581
    """
582
    return self.query.OldStyleQuery(self._GetQueryData(lu),
583
                                    sort_by_name=self.sort_by_name)
584

  
585

  
586 85
def _ShareAll():
587 86
  """Returns a dict declaring all lock levels shared.
588 87

  
......
1405 904
          _ComputeViolatingInstances(old_ipolicy, instances, cfg))
1406 905

  
1407 906

  
1408
def _ExpandItemName(fn, name, kind):
1409
  """Expand an item name.
1410

  
1411
  @param fn: the function to use for expansion
1412
  @param name: requested item name
1413
  @param kind: text description ('Node' or 'Instance')
1414
  @return: the resolved (full) name
1415
  @raise errors.OpPrereqError: if the item is not found
1416

  
1417
  """
1418
  full_name = fn(name)
1419
  if full_name is None:
1420
    raise errors.OpPrereqError("%s '%s' not known" % (kind, name),
1421
                               errors.ECODE_NOENT)
1422
  return full_name
1423

  
1424

  
1425
def _ExpandNodeName(cfg, name):
1426
  """Wrapper over L{_ExpandItemName} for nodes."""
1427
  return _ExpandItemName(cfg.ExpandNodeName, name, "Node")
1428

  
1429

  
1430
def _ExpandInstanceName(cfg, name):
1431
  """Wrapper over L{_ExpandItemName} for instance."""
1432
  return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
1433

  
1434

  
1435 907
def _BuildNetworkHookEnv(name, subnet, gateway, network6, gateway6,
1436 908
                         mac_prefix, tags):
1437 909
  """Builds network related env variables for hooks

Also available in: Unified diff