Revision 36b66e6e

b/lib/cmdlib.py
5677 5677
          raise errors.OpPrereqError("Invalid MAC address specified: %s" %
5678 5678
                                     mac, errors.ECODE_INVAL)
5679 5679
        else:
5680
          # or validate/reserve the current one
5681
          if self.cfg.IsMacInUse(mac):
5680
          try:
5681
            self.cfg.ReserveMAC(mac, self.proc.GetECId())
5682
          except errors.ReservationError:
5682 5683
            raise errors.OpPrereqError("MAC address %s already in use"
5683 5684
                                       " in cluster" % mac,
5684 5685
                                       errors.ECODE_NOTUNIQUE)
......
5958 5959
    # creation job will fail.
5959 5960
    for nic in self.nics:
5960 5961
      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
5961
        nic.mac = self.cfg.GenerateMAC()
5962
        nic.mac = self.cfg.GenerateMAC(self.proc.GetECId())
5962 5963

  
5963 5964
    #### allocator run
5964 5965

  
......
7698 7699
                                     errors.ECODE_INVAL)
7699 7700
        elif nic_mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
7700 7701
          # otherwise generate the mac
7701
          nic_dict['mac'] = self.cfg.GenerateMAC()
7702
          nic_dict['mac'] = self.cfg.GenerateMAC(self.proc.GetECId())
7702 7703
        else:
7703 7704
          # or validate/reserve the current one
7704
          if self.cfg.IsMacInUse(nic_mac):
7705
          try:
7706
            self.cfg.ReserveMAC(nic_mac, self.proc.GetECId())
7707
          except errors.ReservationError:
7705 7708
            raise errors.OpPrereqError("MAC address %s already in use"
7706 7709
                                       " in cluster" % nic_mac,
7707 7710
                                       errors.ECODE_NOTUNIQUE)
b/lib/config.py
137 137
      self._cfg_file = cfg_file
138 138
    self._temporary_ids = TemporaryReservationManager()
139 139
    self._temporary_drbds = {}
140
    self._temporary_macs = set()
140
    self._temporary_macs = TemporaryReservationManager()
141 141
    # Note: in order to prevent errors when resolving our name in
142 142
    # _DistributeConfig, we compute it here once and reuse it; it's
143 143
    # better to raise an error before starting to modify the config
......
154 154
    """
155 155
    return os.path.exists(constants.CLUSTER_CONF_FILE)
156 156

  
157
  def _GenerateOneMAC(self):
158
    """Generate one mac address
159

  
160
    """
161
    prefix = self._config_data.cluster.mac_prefix
162
    byte1 = random.randrange(0, 256)
163
    byte2 = random.randrange(0, 256)
164
    byte3 = random.randrange(0, 256)
165
    mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
166
    return mac
167

  
157 168
  @locking.ssynchronized(_config_lock, shared=1)
158
  def GenerateMAC(self):
169
  def GenerateMAC(self, ec_id):
159 170
    """Generate a MAC for an instance.
160 171

  
161 172
    This should check the current instances for duplicates.
162 173

  
163 174
    """
164
    prefix = self._config_data.cluster.mac_prefix
165
    all_macs = self._AllMACs()
166
    retries = 64
167
    while retries > 0:
168
      byte1 = random.randrange(0, 256)
169
      byte2 = random.randrange(0, 256)
170
      byte3 = random.randrange(0, 256)
171
      mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
172
      if mac not in all_macs and mac not in self._temporary_macs:
173
        break
174
      retries -= 1
175
    else:
176
      raise errors.ConfigurationError("Can't generate unique MAC")
177
    self._temporary_macs.add(mac)
178
    return mac
175
    existing = self._AllMACs()
176
    return self._temporary_ids.Generate(existing, self._GenerateOneMAC, ec_id)
179 177

  
180 178
  @locking.ssynchronized(_config_lock, shared=1)
181
  def IsMacInUse(self, mac):
182
    """Predicate: check if the specified MAC is in use in the Ganeti cluster.
179
  def ReserveMAC(self, mac, ec_id):
180
    """Reserve a MAC for an instance.
183 181

  
184 182
    This only checks instances managed by this cluster, it does not
185 183
    check for potential collisions elsewhere.
186 184

  
187 185
    """
188 186
    all_macs = self._AllMACs()
189
    return mac in all_macs or mac in self._temporary_macs
187
    if mac in all_macs:
188
      raise errors.ReservationError("mac already in use")
189
    else:
190
      self._temporary_macs.Reserve(mac, ec_id)
190 191

  
191 192
  @locking.ssynchronized(_config_lock, shared=1)
192 193
  def GenerateDRBDSecret(self):
......
799 800
    self._config_data.instances[instance.name] = instance
800 801
    self._config_data.cluster.serial_no += 1
801 802
    self._UnlockedReleaseDRBDMinors(instance.name)
802
    for nic in instance.nics:
803
      self._temporary_macs.discard(nic.mac)
804 803
    self._WriteConfig()
805 804

  
806 805
  def _EnsureUUID(self, item, ec_id):
......
1420 1419

  
1421 1420
    if isinstance(target, objects.Instance):
1422 1421
      self._UnlockedReleaseDRBDMinors(target.name)
1423
      for nic in target.nics:
1424
        self._temporary_macs.discard(nic.mac)
1425 1422

  
1426 1423
    self._WriteConfig(feedback_fn=feedback_fn)
1427 1424

  
......
1431 1428

  
1432 1429
    """
1433 1430
    self._temporary_ids.DropECReservations(ec_id)
1431
    self._temporary_macs.DropECReservations(ec_id)
1434 1432

  

Also available in: Unified diff