Revision 1a732a74

b/Makefile.am
308 308
	lib/client/gnt_storage.py
309 309

  
310 310
cmdlib_PYTHON = \
311
	lib/cmdlib/__init__.py
311
	lib/cmdlib/__init__.py \
312
	lib/cmdlib/common.py \
313
	lib/cmdlib/base.py
312 314

  
313 315
hypervisor_PYTHON = \
314 316
	lib/hypervisor/__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
b/lib/cmdlib/base.py
1
#
2
#
3

  
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21

  
22
"""Base classes and functions for cmdlib."""
23

  
24
import logging
25

  
26
from ganeti import errors
27
from ganeti import constants
28
from ganeti import locking
29
from ganeti import query
30
from ganeti import utils
31
from ganeti.cmdlib.common import _ExpandInstanceName
32

  
33

  
34
class ResultWithJobs:
35
  """Data container for LU results with jobs.
36

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

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

  
46
    Additional return values can be specified as keyword arguments.
47

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

  
51
    """
52
    self.jobs = jobs
53
    self.other = kwargs
54

  
55

  
56
class LogicalUnit(object):
57
  """Logical Unit base class.
58

  
59
  Subclasses must follow these rules:
60
    - implement ExpandNames
61
    - implement CheckPrereq (except when tasklets are used)
62
    - implement Exec (except when tasklets are used)
63
    - implement BuildHooksEnv
64
    - implement BuildHooksNodes
65
    - redefine HPATH and HTYPE
66
    - optionally redefine their run requirements:
67
        REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
68

  
69
  Note that all commands require root permissions.
70

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

  
74
  """
75
  HPATH = None
76
  HTYPE = None
77
  REQ_BGL = True
78

  
79
  def __init__(self, processor, op, context, rpc_runner):
80
    """Constructor for LogicalUnit.
81

  
82
    This needs to be overridden in derived classes in order to check op
83
    validity.
84

  
85
    """
86
    self.proc = processor
87
    self.op = op
88
    self.cfg = context.cfg
89
    self.glm = context.glm
90
    # readability alias
91
    self.owned_locks = context.glm.list_owned
92
    self.context = context
93
    self.rpc = rpc_runner
94

  
95
    # Dictionaries used to declare locking needs to mcpu
96
    self.needed_locks = None
97
    self.share_locks = dict.fromkeys(locking.LEVELS, 0)
98
    self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False)
99

  
100
    self.add_locks = {}
101
    self.remove_locks = {}
102

  
103
    # Used to force good behavior when calling helper functions
104
    self.recalculate_locks = {}
105

  
106
    # logging
107
    self.Log = processor.Log # pylint: disable=C0103
108
    self.LogWarning = processor.LogWarning # pylint: disable=C0103
109
    self.LogInfo = processor.LogInfo # pylint: disable=C0103
110
    self.LogStep = processor.LogStep # pylint: disable=C0103
111
    # support for dry-run
112
    self.dry_run_result = None
113
    # support for generic debug attribute
114
    if (not hasattr(self.op, "debug_level") or
115
        not isinstance(self.op.debug_level, int)):
116
      self.op.debug_level = 0
117

  
118
    # Tasklets
119
    self.tasklets = None
120

  
121
    # Validate opcode parameters and set defaults
122
    self.op.Validate(True)
123

  
124
    self.CheckArguments()
125

  
126
  def CheckArguments(self):
127
    """Check syntactic validity for the opcode arguments.
128

  
129
    This method is for doing a simple syntactic check and ensure
130
    validity of opcode parameters, without any cluster-related
131
    checks. While the same can be accomplished in ExpandNames and/or
132
    CheckPrereq, doing these separate is better because:
133

  
134
      - ExpandNames is left as as purely a lock-related function
135
      - CheckPrereq is run after we have acquired locks (and possible
136
        waited for them)
137

  
138
    The function is allowed to change the self.op attribute so that
139
    later methods can no longer worry about missing parameters.
140

  
141
    """
142
    pass
143

  
144
  def ExpandNames(self):
145
    """Expand names for this LU.
146

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

  
152
    LUs which implement this method must also populate the self.needed_locks
153
    member, as a dict with lock levels as keys, and a list of needed lock names
154
    as values. Rules:
155

  
156
      - use an empty dict if you don't need any lock
157
      - if you don't need any lock at a particular level omit that
158
        level (note that in this case C{DeclareLocks} won't be called
159
        at all for that level)
160
      - if you need locks at a level, but you can't calculate it in
161
        this function, initialise that level with an empty list and do
162
        further processing in L{LogicalUnit.DeclareLocks} (see that
163
        function's docstring)
164
      - don't put anything for the BGL level
165
      - if you want all locks at a level use L{locking.ALL_SET} as a value
166

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

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

  
175
    Examples::
176

  
177
      # Acquire all nodes and one instance
178
      self.needed_locks = {
179
        locking.LEVEL_NODE: locking.ALL_SET,
180
        locking.LEVEL_INSTANCE: ['instance1.example.com'],
181
      }
182
      # Acquire just two nodes
183
      self.needed_locks = {
184
        locking.LEVEL_NODE: ['node1.example.com', 'node2.example.com'],
185
      }
186
      # Acquire no locks
187
      self.needed_locks = {} # No, you can't leave it to the default value None
188

  
189
    """
190
    # The implementation of this method is mandatory only if the new LU is
191
    # concurrent, so that old LUs don't need to be changed all at the same
192
    # time.
193
    if self.REQ_BGL:
194
      self.needed_locks = {} # Exclusive LUs don't need locks.
195
    else:
196
      raise NotImplementedError
197

  
198
  def DeclareLocks(self, level):
199
    """Declare LU locking needs for a level
200

  
201
    While most LUs can just declare their locking needs at ExpandNames time,
202
    sometimes there's the need to calculate some locks after having acquired
203
    the ones before. This function is called just before acquiring locks at a
204
    particular level, but after acquiring the ones at lower levels, and permits
205
    such calculations. It can be used to modify self.needed_locks, and by
206
    default it does nothing.
207

  
208
    This function is only called if you have something already set in
209
    self.needed_locks for the level.
210

  
211
    @param level: Locking level which is going to be locked
212
    @type level: member of L{ganeti.locking.LEVELS}
213

  
214
    """
215

  
216
  def CheckPrereq(self):
217
    """Check prerequisites for this LU.
218

  
219
    This method should check that the prerequisites for the execution
220
    of this LU are fulfilled. It can do internode communication, but
221
    it should be idempotent - no cluster or system changes are
222
    allowed.
223

  
224
    The method should raise errors.OpPrereqError in case something is
225
    not fulfilled. Its return value is ignored.
226

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

  
230
    """
231
    if self.tasklets is not None:
232
      for (idx, tl) in enumerate(self.tasklets):
233
        logging.debug("Checking prerequisites for tasklet %s/%s",
234
                      idx + 1, len(self.tasklets))
235
        tl.CheckPrereq()
236
    else:
237
      pass
238

  
239
  def Exec(self, feedback_fn):
240
    """Execute the LU.
241

  
242
    This method should implement the actual work. It should raise
243
    errors.OpExecError for failures that are somewhat dealt with in
244
    code, or expected.
245

  
246
    """
247
    if self.tasklets is not None:
248
      for (idx, tl) in enumerate(self.tasklets):
249
        logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
250
        tl.Exec(feedback_fn)
251
    else:
252
      raise NotImplementedError
253

  
254
  def BuildHooksEnv(self):
255
    """Build hooks environment for this LU.
256

  
257
    @rtype: dict
258
    @return: Dictionary containing the environment that will be used for
259
      running the hooks for this LU. The keys of the dict must not be prefixed
260
      with "GANETI_"--that'll be added by the hooks runner. The hooks runner
261
      will extend the environment with additional variables. If no environment
262
      should be defined, an empty dictionary should be returned (not C{None}).
263
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
264
      will not be called.
265

  
266
    """
267
    raise NotImplementedError
268

  
269
  def BuildHooksNodes(self):
270
    """Build list of nodes to run LU's hooks.
271

  
272
    @rtype: tuple; (list, list)
273
    @return: Tuple containing a list of node names on which the hook
274
      should run before the execution and a list of node names on which the
275
      hook should run after the execution. No nodes should be returned as an
276
      empty list (and not None).
277
    @note: If the C{HPATH} attribute of the LU class is C{None}, this function
278
      will not be called.
279

  
280
    """
281
    raise NotImplementedError
282

  
283
  def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
284
    """Notify the LU about the results of its hooks.
285

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

  
292
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
293
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
294
    @param hook_results: the results of the multi-node hooks rpc call
295
    @param feedback_fn: function used send feedback back to the caller
296
    @param lu_result: the previous Exec result this LU had, or None
297
        in the PRE phase
298
    @return: the new Exec result, based on the previous result
299
        and hook results
300

  
301
    """
302
    # API must be kept, thus we ignore the unused argument and could
303
    # be a function warnings
304
    # pylint: disable=W0613,R0201
305
    return lu_result
306

  
307
  def _ExpandAndLockInstance(self):
308
    """Helper function to expand and lock an instance.
309

  
310
    Many LUs that work on an instance take its name in self.op.instance_name
311
    and need to expand it and then declare the expanded name for locking. This
312
    function does it, and then updates self.op.instance_name to the expanded
313
    name. It also initializes needed_locks as a dict, if this hasn't been done
314
    before.
315

  
316
    """
317
    if self.needed_locks is None:
318
      self.needed_locks = {}
319
    else:
320
      assert locking.LEVEL_INSTANCE not in self.needed_locks, \
321
        "_ExpandAndLockInstance called with instance-level locks set"
322
    self.op.instance_name = _ExpandInstanceName(self.cfg,
323
                                                self.op.instance_name)
324
    self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
325

  
326
  def _LockInstancesNodes(self, primary_only=False,
327
                          level=locking.LEVEL_NODE):
328
    """Helper function to declare instances' nodes for locking.
329

  
330
    This function should be called after locking one or more instances to lock
331
    their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
332
    with all primary or secondary nodes for instances already locked and
333
    present in self.needed_locks[locking.LEVEL_INSTANCE].
334

  
335
    It should be called from DeclareLocks, and for safety only works if
336
    self.recalculate_locks[locking.LEVEL_NODE] is set.
337

  
338
    In the future it may grow parameters to just lock some instance's nodes, or
339
    to just lock primaries or secondary nodes, if needed.
340

  
341
    If should be called in DeclareLocks in a way similar to::
342

  
343
      if level == locking.LEVEL_NODE:
344
        self._LockInstancesNodes()
345

  
346
    @type primary_only: boolean
347
    @param primary_only: only lock primary nodes of locked instances
348
    @param level: Which lock level to use for locking nodes
349

  
350
    """
351
    assert level in self.recalculate_locks, \
352
      "_LockInstancesNodes helper function called with no nodes to recalculate"
353

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

  
356
    # For now we'll replace self.needed_locks[locking.LEVEL_NODE], but in the
357
    # future we might want to have different behaviors depending on the value
358
    # of self.recalculate_locks[locking.LEVEL_NODE]
359
    wanted_nodes = []
360
    locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
361
    for _, instance in self.cfg.GetMultiInstanceInfo(locked_i):
362
      wanted_nodes.append(instance.primary_node)
363
      if not primary_only:
364
        wanted_nodes.extend(instance.secondary_nodes)
365

  
366
    if self.recalculate_locks[level] == constants.LOCKS_REPLACE:
367
      self.needed_locks[level] = wanted_nodes
368
    elif self.recalculate_locks[level] == constants.LOCKS_APPEND:
369
      self.needed_locks[level].extend(wanted_nodes)
370
    else:
371
      raise errors.ProgrammerError("Unknown recalculation mode")
372

  
373
    del self.recalculate_locks[level]
374

  
375

  
376
class NoHooksLU(LogicalUnit): # pylint: disable=W0223
377
  """Simple LU which runs no hooks.
378

  
379
  This LU is intended as a parent for other LogicalUnits which will
380
  run no hooks, in order to reduce duplicate code.
381

  
382
  """
383
  HPATH = None
384
  HTYPE = None
385

  
386
  def BuildHooksEnv(self):
387
    """Empty BuildHooksEnv for NoHooksLu.
388

  
389
    This just raises an error.
390

  
391
    """
392
    raise AssertionError("BuildHooksEnv called for NoHooksLUs")
393

  
394
  def BuildHooksNodes(self):
395
    """Empty BuildHooksNodes for NoHooksLU.
396

  
397
    """
398
    raise AssertionError("BuildHooksNodes called for NoHooksLU")
399

  
400

  
401
class Tasklet:
402
  """Tasklet base class.
403

  
404
  Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
405
  they can mix legacy code with tasklets. Locking needs to be done in the LU,
406
  tasklets know nothing about locks.
407

  
408
  Subclasses must follow these rules:
409
    - Implement CheckPrereq
410
    - Implement Exec
411

  
412
  """
413
  def __init__(self, lu):
414
    self.lu = lu
415

  
416
    # Shortcuts
417
    self.cfg = lu.cfg
418
    self.rpc = lu.rpc
419

  
420
  def CheckPrereq(self):
421
    """Check prerequisites for this tasklets.
422

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

  
427
    The method should raise errors.OpPrereqError in case something is not
428
    fulfilled. Its return value is ignored.
429

  
430
    This method should also update all parameters to their canonical form if it
431
    hasn't been done before.
432

  
433
    """
434
    pass
435

  
436
  def Exec(self, feedback_fn):
437
    """Execute the tasklet.
438

  
439
    This method should implement the actual work. It should raise
440
    errors.OpExecError for failures that are somewhat dealt with in code, or
441
    expected.
442

  
443
    """
444
    raise NotImplementedError
445

  
446

  
447
class _QueryBase:
448
  """Base for query utility classes.
449

  
450
  """
451
  #: Attribute holding field definitions
452
  FIELDS = None
453

  
454
  #: Field to sort by
455
  SORT_FIELD = "name"
456

  
457
  def __init__(self, qfilter, fields, use_locking):
458
    """Initializes this class.
459

  
460
    """
461
    self.use_locking = use_locking
462

  
463
    self.query = query.Query(self.FIELDS, fields, qfilter=qfilter,
464
                             namefield=self.SORT_FIELD)
465
    self.requested_data = self.query.RequestedData()
466
    self.names = self.query.RequestedNames()
467

  
468
    # Sort only if no names were requested
469
    self.sort_by_name = not self.names
470

  
471
    self.do_locking = None
472
    self.wanted = None
473

  
474
  def _GetNames(self, lu, all_names, lock_level):
475
    """Helper function to determine names asked for in the query.
476

  
477
    """
478
    if self.do_locking:
479
      names = lu.owned_locks(lock_level)
480
    else:
481
      names = all_names
482

  
483
    if self.wanted == locking.ALL_SET:
484
      assert not self.names
485
      # caller didn't specify names, so ordering is not important
486
      return utils.NiceSort(names)
487

  
488
    # caller specified names and we must keep the same order
489
    assert self.names
490
    assert not self.do_locking or lu.glm.is_owned(lock_level)
491

  
492
    missing = set(self.wanted).difference(names)
493
    if missing:
494
      raise errors.OpExecError("Some items were removed before retrieving"
495
                               " their data: %s" % missing)
496

  
497
    # Return expanded names
498
    return self.wanted
499

  
500
  def ExpandNames(self, lu):
501
    """Expand names for this query.
502

  
503
    See L{LogicalUnit.ExpandNames}.
504

  
505
    """
506
    raise NotImplementedError()
507

  
508
  def DeclareLocks(self, lu, level):
509
    """Declare locks for this query.
510

  
511
    See L{LogicalUnit.DeclareLocks}.
512

  
513
    """
514
    raise NotImplementedError()
515

  
516
  def _GetQueryData(self, lu):
517
    """Collects all data for this query.
518

  
519
    @return: Query data object
520

  
521
    """
522
    raise NotImplementedError()
523

  
524
  def NewStyleQuery(self, lu):
525
    """Collect data and execute query.
526

  
527
    """
528
    return query.GetQueryResponse(self.query, self._GetQueryData(lu),
529
                                  sort_by_name=self.sort_by_name)
530

  
531
  def OldStyleQuery(self, lu):
532
    """Collect data and execute query.
533

  
534
    """
535
    return self.query.OldStyleQuery(self._GetQueryData(lu),
536
                                    sort_by_name=self.sort_by_name)
b/lib/cmdlib/common.py
1
#
2
#
3

  
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21

  
22
"""Common functions used by multiple logical units."""
23

  
24
from ganeti import errors
25

  
26

  
27
def _ExpandItemName(fn, name, kind):
28
  """Expand an item name.
29

  
30
  @param fn: the function to use for expansion
31
  @param name: requested item name
32
  @param kind: text description ('Node' or 'Instance')
33
  @return: the resolved (full) name
34
  @raise errors.OpPrereqError: if the item is not found
35

  
36
  """
37
  full_name = fn(name)
38
  if full_name is None:
39
    raise errors.OpPrereqError("%s '%s' not known" % (kind, name),
40
                               errors.ECODE_NOENT)
41
  return full_name
42

  
43

  
44
def _ExpandInstanceName(cfg, name):
45
  """Wrapper over L{_ExpandItemName} for instance."""
46
  return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
47

  
48

  
49
def _ExpandNodeName(cfg, name):
50
  """Wrapper over L{_ExpandItemName} for nodes."""
51
  return _ExpandItemName(cfg.ExpandNodeName, name, "Node")

Also available in: Unified diff