Revision cc19798f

b/lib/cmdlib.py
5246 5246
    self.lock_all = self.op.auto_promote and self.might_demote
5247 5247
    self.lock_instances = self.op.secondary_ip is not None
5248 5248

  
5249
  def _InstanceFilter(self, instance):
5250
    """Filter for getting affected instances.
5251

  
5252
    """
5253
    return (instance.disk_template in constants.DTS_INT_MIRROR and
5254
            self.op.node_name in instance.all_nodes)
5255

  
5249 5256
  def ExpandNames(self):
5250 5257
    if self.lock_all:
5251 5258
      self.needed_locks = {locking.LEVEL_NODE: locking.ALL_SET}
......
5253 5260
      self.needed_locks = {locking.LEVEL_NODE: self.op.node_name}
5254 5261

  
5255 5262
    if self.lock_instances:
5256
      self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
5257

  
5258
  def DeclareLocks(self, level):
5259
    # If we have locked all instances, before waiting to lock nodes, release
5260
    # all the ones living on nodes unrelated to the current operation.
5261
    if level == locking.LEVEL_NODE and self.lock_instances:
5262
      self.affected_instances = []
5263
      if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
5264
        instances_keep = []
5265

  
5266
        # Build list of instances to release
5267
        locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
5268
        for instance_name, instance in self.cfg.GetMultiInstanceInfo(locked_i):
5269
          if (instance.disk_template in constants.DTS_INT_MIRROR and
5270
              self.op.node_name in instance.all_nodes):
5271
            instances_keep.append(instance_name)
5272
            self.affected_instances.append(instance)
5273

  
5274
        _ReleaseLocks(self, locking.LEVEL_INSTANCE, keep=instances_keep)
5275

  
5276
        assert (set(self.owned_locks(locking.LEVEL_INSTANCE)) ==
5277
                set(instances_keep))
5263
      self.needed_locks[locking.LEVEL_INSTANCE] = \
5264
        frozenset(self.cfg.GetInstancesInfoByFilter(self._InstanceFilter))
5278 5265

  
5279 5266
  def BuildHooksEnv(self):
5280 5267
    """Build hooks env.
......
5306 5293
    """
5307 5294
    node = self.node = self.cfg.GetNodeInfo(self.op.node_name)
5308 5295

  
5296
    if self.lock_instances:
5297
      affected_instances = \
5298
        self.cfg.GetInstancesInfoByFilter(self._InstanceFilter)
5299

  
5300
      # Verify instance locks
5301
      owned_instances = self.owned_locks(locking.LEVEL_INSTANCE)
5302
      wanted_instances = frozenset(affected_instances.keys())
5303
      if wanted_instances - owned_instances:
5304
        raise errors.OpPrereqError("Instances affected by changing node %s's"
5305
                                   " secondary IP address have changed since"
5306
                                   " locks were acquired, wanted '%s', have"
5307
                                   " '%s'; retry the operation" %
5308
                                   (self.op.node_name,
5309
                                    utils.CommaJoin(wanted_instances),
5310
                                    utils.CommaJoin(owned_instances)),
5311
                                   errors.ECODE_STATE)
5312
    else:
5313
      affected_instances = None
5314

  
5309 5315
    if (self.op.master_candidate is not None or
5310 5316
        self.op.drained is not None or
5311 5317
        self.op.offline is not None):
......
5416 5422
        raise errors.OpPrereqError("Cannot change the secondary ip on a single"
5417 5423
                                   " homed cluster", errors.ECODE_INVAL)
5418 5424

  
5425
      assert not (frozenset(affected_instances) -
5426
                  self.owned_locks(locking.LEVEL_INSTANCE))
5427

  
5419 5428
      if node.offline:
5420
        if self.affected_instances:
5421
          raise errors.OpPrereqError("Cannot change secondary ip: offline"
5422
                                     " node has instances (%s) configured"
5423
                                     " to use it" % self.affected_instances)
5429
        if affected_instances:
5430
          raise errors.OpPrereqError("Cannot change secondary IP address:"
5431
                                     " offline node has instances (%s)"
5432
                                     " configured to use it" %
5433
                                     utils.CommaJoin(affected_instances.keys()))
5424 5434
      else:
5425 5435
        # On online nodes, check that no instances are running, and that
5426 5436
        # the node has the new ip and we can reach it.
5427
        for instance in self.affected_instances:
5437
        for instance in affected_instances.values():
5428 5438
          _CheckInstanceDown(self, instance, "cannot change secondary ip")
5429 5439

  
5430 5440
        _CheckNodeHasSecondaryIP(self, node.name, self.op.secondary_ip, True)
b/lib/config.py
1334 1334
                    for instance in self._UnlockedGetInstanceList()])
1335 1335
    return my_dict
1336 1336

  
1337
  @locking.ssynchronized(_config_lock, shared=1)
1338
  def GetInstancesInfoByFilter(self, filter_fn):
1339
    """Get instance configuration with a filter.
1340

  
1341
    @type filter_fn: callable
1342
    @param filter_fn: Filter function receiving instance object as parameter,
1343
      returning boolean. Important: this function is called while the
1344
      configuration locks is held. It must not do any complex work or call
1345
      functions potentially leading to a deadlock. Ideally it doesn't call any
1346
      other functions and just compares instance attributes.
1347

  
1348
    """
1349
    return dict((name, inst)
1350
                for (name, inst) in self._config_data.instances.items()
1351
                if filter_fn(inst))
1352

  
1337 1353
  @locking.ssynchronized(_config_lock)
1338 1354
  def AddNode(self, node, ec_id):
1339 1355
    """Add a node to the configuration.

Also available in: Unified diff