Revision e5370111

b/doc/design-network.rst
88 88
bitfields, the length of the network size each:
89 89

  
90 90
``reservations``
91
  This field holds all IP addresses reserved by Ganeti instances, as
92
  well as cluster IP addresses (node addresses + cluster master)
91
  This field holds all IP addresses reserved by Ganeti instances.
93 92

  
94 93
``external reservations``
95 94
  This field holds all IP addresses that are manually reserved by the
96
  administrator, because some other equipment is using them outside the
97
  scope of Ganeti.
95
  administrator (external gateway, IPs of external servers, etc) or
96
  automatically by ganeti (the network/broadcast addresses,
97
  Cluster IPs (node addresses + cluster master)). These IPs are excluded
98
  from the IP pool and cannot be assigned automatically by ganeti to
99
  instances (via ip=pool).
98 100

  
99 101
The bitfields are implemented using the python-bitarray package for
100 102
space efficiency and their binary value stored base64-encoded for JSON
101 103
compatibility. This approach gives relatively compact representations
102 104
even for large IPv4 networks (e.g. /20).
103 105

  
104
Ganeti-owned IP addresses (node + master IPs) are reserved automatically
105
if the cluster's data network itself is placed under pool management.
106
Cluster IP addresses (node + master IPs) are reserved automatically
107
as external if the cluster's data network itself is placed under
108
pool management.
106 109

  
107 110
Helper ConfigWriter methods provide free IP address generation and
108 111
reservation, using a TemporaryReservationManager.
......
129 132

  
130 133
We also introduce a new ``ip`` address value, ``constants.NIC_IP_POOL``,
131 134
that specifies that a given NIC's IP address should be obtained using
132
the IP address pool of the specified network. This value is only valid
135
the first available IP address inside the pool of the specified network.
136
(reservations OR external_reservations). This value is only valid
133 137
for NICs belonging to a network. A NIC's IP address can also be
134 138
specified manually, as long as it is contained in the network the NIC
135
is connected to.
139
is connected to. In case this IP is externally reserved, Ganeti will produce
140
an error which the user can override if explicitly requested. Of course
141
this IP will be reserved and will not be able to be assigned to another
142
instance.
136 143

  
137 144

  
138 145
Hooks
b/lib/cmdlib/instance.py
1041 1041
            self.LogInfo("Chose IP %s from network %s", nic.ip, nobj.name)
1042 1042
          else:
1043 1043
            try:
1044
              self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId())
1044
              self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId(),
1045
                                 check=self.op.conflicts_check)
1045 1046
            except errors.ReservationError:
1046 1047
              raise errors.OpPrereqError("IP address %s already in use"
1047 1048
                                         " or does not belong to network %s" %
......
2586 2587
        # Reserve new IP if in the new network if any
2587 2588
        elif new_net_uuid:
2588 2589
          try:
2589
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
2590
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId(),
2591
                               check=self.op.conflicts_check)
2590 2592
            self.LogInfo("Reserving IP %s in network %s",
2591 2593
                         new_ip, new_net_obj.name)
2592 2594
          except errors.ReservationError:
b/lib/cmdlib/network.py
365 365
    if self.op.add_reserved_ips:
366 366
      for ip in self.op.add_reserved_ips:
367 367
        try:
368
          if self.pool.IsReserved(ip):
369
            self.LogWarning("IP address %s is already reserved", ip)
370
          else:
371
            self.pool.Reserve(ip, external=True)
368
          self.pool.Reserve(ip, external=True)
372 369
        except errors.AddressPoolError, err:
373 370
          self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
374 371

  
......
378 375
          self.LogWarning("Cannot unreserve Gateway's IP")
379 376
          continue
380 377
        try:
381
          if not self.pool.IsReserved(ip):
382
            self.LogWarning("IP address %s is already unreserved", ip)
383
          else:
384
            self.pool.Release(ip, external=True)
378
          self.pool.Release(ip, external=True)
385 379
        except errors.AddressPoolError, err:
386 380
          self.LogWarning("Cannot release IP address %s: %s", ip, err)
387 381

  
b/lib/config.py
384 384
    _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
385 385
    return address
386 386

  
387
  def _UnlockedReserveIp(self, net_uuid, address, ec_id):
387
  def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
388 388
    """Reserve a given IPv4 address for use by an instance.
389 389

  
390 390
    """
......
392 392
    pool = network.AddressPool(nobj)
393 393
    try:
394 394
      isreserved = pool.IsReserved(address)
395
      isextreserved = pool.IsReserved(address, external=True)
395 396
    except errors.AddressPoolError:
396 397
      raise errors.ReservationError("IP address not in network")
397 398
    if isreserved:
398 399
      raise errors.ReservationError("IP address already in use")
400
    if check and isextreserved:
401
      raise errors.ReservationError("IP is externally reserved")
399 402

  
400 403
    return self._temporary_ips.Reserve(ec_id,
401 404
                                       (constants.RESERVE_ACTION,
402 405
                                        address, net_uuid))
403 406

  
404 407
  @locking.ssynchronized(_config_lock, shared=1)
405
  def ReserveIp(self, net_uuid, address, ec_id):
408
  def ReserveIp(self, net_uuid, address, ec_id, check=True):
406 409
    """Reserve a given IPv4 address for use by an instance.
407 410

  
408 411
    """
409 412
    if net_uuid:
410
      return self._UnlockedReserveIp(net_uuid, address, ec_id)
413
      return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
411 414

  
412 415
  @locking.ssynchronized(_config_lock, shared=1)
413 416
  def ReserveLV(self, lv_name, ec_id):
b/lib/network.py
153 153
  def Validate(self):
154 154
    assert len(self.reservations) == self._GetSize()
155 155
    assert len(self.ext_reservations) == self._GetSize()
156
    all_res = self.reservations & self.ext_reservations
157
    assert not all_res.any()
158 156

  
159 157
    if self.gateway is not None:
160 158
      assert self.gateway in self.network
......
188 186
    """
189 187
    return self.all_reservations.to01().replace("1", "X").replace("0", ".")
190 188

  
191
  def IsReserved(self, address):
189
  def IsReserved(self, address, external=False):
192 190
    """Checks if the given IP is reserved.
193 191

  
194 192
    """
195 193
    idx = self._GetAddrIndex(address)
196
    return self.all_reservations[idx]
194
    if external:
195
      return self.ext_reservations[idx]
196
    else:
197
      return self.reservations[idx]
197 198

  
198 199
  def Reserve(self, address, external=False):
199 200
    """Mark an address as used.
200 201

  
201 202
    """
202
    if self.IsReserved(address):
203
      raise errors.AddressPoolError("%s is already reserved" % address)
203
    if self.IsReserved(address, external):
204
      if external:
205
        msg = "IP %s is already externally reserved" % address
206
      else:
207
        msg = "IP %s is already used by an instance" % address
208
      raise errors.AddressPoolError(msg)
209

  
204 210
    self._Mark(address, external=external)
205 211

  
206 212
  def Release(self, address, external=False):
207 213
    """Release a given address reservation.
208 214

  
209 215
    """
216
    if not self.IsReserved(address, external):
217
      if external:
218
        msg = "IP %s is not externally reserved" % address
219
      else:
220
        msg = "IP %s is not used by an instance" % address
221
      raise errors.AddressPoolError(msg)
222

  
210 223
    self._Mark(address, value=False, external=external)
211 224

  
212 225
  def GetFreeAddress(self):
b/man/gnt-instance.rst
132 132
    connected to the said network. ``--no-conflicts-check`` can be used
133 133
    to override this check. The special value **pool** causes Ganeti to
134 134
    select an IP from the the network the NIC is or will be connected to.
135
    One can pick an externally reserved IP of a network along with
136
    ``--no-conflict-check``. Note that this IP cannot be assigned to
137
    any other instance until it gets released.
135 138

  
136 139
mode
137 140
    specifies the connection mode for this NIC: routed, bridged or

Also available in: Unified diff