def _LockAndExecLU(self, lu, level, calc_timeout, pending=None):
    """Execute a Logical Unit, with the needed locks.

    This is a recursive function that starts locking the given level, and
    proceeds up, till there are no more locks to acquire. Then it executes the
    given LU and its opcodes.

    """
    pending = pending or []
    logging.debug("Looking at locks of level %s, still need to obtain %s",
                  level, pending)
    adding_locks = level in lu.add_locks
    acquiring_locks = level in lu.needed_locks

    if level not in locking.LEVELS:
      if pending:
        self._RequestAndWait(pending, calc_timeout())
        lu.cfg.OutDate()
        lu.wconfdlocks = self.wconfd.Client().ListLocks(self._wconfdcontext)
        pending = []

      logging.debug("Finished acquiring locks")

      if self._cbs:
        self._cbs.NotifyStart()

      try:
        result = self._ExecLU(lu)
      except errors.OpPrereqError, err:
        (_, ecode) = err.args
        if ecode != errors.ECODE_TEMP_NORES:
          raise
        logging.debug("Temporarily out of resources; will retry internally")
        try:
          lu.PrepareRetry(self.Log)
          if self._cbs:
            self._cbs.NotifyRetry()
        except errors.OpRetryNotSupportedError:
          logging.debug("LU does not know how to retry.")
          raise err
        raise LockAcquireTimeout()
      except AssertionError, err:
        # this is a bit ugly, as we don't know from which phase
        # (prereq, exec) this comes; but it's better than an exception
        # with no information
        (_, _, tb) = sys.exc_info()
        err_info = traceback.format_tb(tb)
        del tb
        logging.exception("Detected AssertionError")
        raise errors.OpExecError("Internal assertion error: please report"
                                 " this as a bug.\nError message: '%s';"
                                 " location:\n%s" % (str(err), err_info[-1]))
      return result

    # Determine if the acquiring is opportunistic up front
    opportunistic = lu.opportunistic_locks[level]

    dont_collate = lu.dont_collate_locks[level]

    if dont_collate and pending:
      self._RequestAndWait(pending, calc_timeout())
      lu.cfg.OutDate()
      lu.wconfdlocks = self.wconfd.Client().ListLocks(self._wconfdcontext)
      pending = []

    if adding_locks and opportunistic:
      # We could simultaneously acquire locks opportunistically and add new
      # ones, but that would require altering the API, and no use cases are
      # present in the system at the moment.
      raise NotImplementedError("Can't opportunistically acquire locks when"
                                " adding new ones")

    if adding_locks and acquiring_locks and \
       lu.needed_locks[level] is locking.ALL_SET:
      # It would also probably be possible to acquire all locks of a certain
      # type while adding new locks, but there is no use case at the moment.
      raise NotImplementedError("Can't request all locks of a certain level"
                                " and add new locks")

    if adding_locks or acquiring_locks:
      self._CheckLocksEnabled()

      lu.DeclareLocks(level)
      share = lu.share_locks[level]
      opportunistic_count = lu.opportunistic_locks_count[level]

      try:
        if acquiring_locks:
          needed_locks = _LockList(lu.needed_locks[level])
        else:
          needed_locks = []

        if adding_locks:
          needed_locks.extend(_LockList(lu.add_locks[level]))

        timeout = calc_timeout()
        if timeout is not None and not opportunistic:
          pending = pending + self._AcquireLocks(level, needed_locks, share,
                                                 opportunistic, timeout,
                                                 request_only=True)
        else:
          if pending:
            self._RequestAndWait(pending, calc_timeout())
            lu.cfg.OutDate()
            lu.wconfdlocks = self.wconfd.Client().ListLocks(self._wconfdcontext)
            pending = []
          self._AcquireLocks(level, needed_locks, share, opportunistic,
                             timeout,
                             opportunistic_count=opportunistic_count)
          lu.wconfdlocks = self.wconfd.Client().ListLocks(self._wconfdcontext)

        result = self._LockAndExecLU(lu, level + 1, calc_timeout,
                                     pending=pending)
      finally:
        levelname = locking.LEVEL_NAMES[level]
        logging.debug("Freeing locks at level %s for %s",
                      levelname, self._wconfdcontext)
        self.wconfd.Client().FreeLocksLevel(self._wconfdcontext, levelname)
    else:
      result = self._LockAndExecLU(lu, level + 1, calc_timeout, pending=pending)

    return result

  # pylint: disable=R0201
dataset/ETHPy150Open ganeti/ganeti/lib/mcpu.py/Processor._LockAndExecLU/operator `==` -> `is`