bitfields, the length of the network size each:
``reservations``
- This field holds all IP addresses reserved by Ganeti instances, as
- well as cluster IP addresses (node addresses + cluster master)
+ This field holds all IP addresses reserved by Ganeti instances.
``external reservations``
This field holds all IP addresses that are manually reserved by the
- administrator, because some other equipment is using them outside the
- scope of Ganeti.
+ administrator (external gateway, IPs of external servers, etc) or
+ automatically by ganeti (the network/broadcast addresses,
+ Cluster IPs (node addresses + cluster master)). These IPs are excluded
+ from the IP pool and cannot be assigned automatically by ganeti to
+ instances (via ip=pool).
The bitfields are implemented using the python-bitarray package for
space efficiency and their binary value stored base64-encoded for JSON
compatibility. This approach gives relatively compact representations
even for large IPv4 networks (e.g. /20).
-Ganeti-owned IP addresses (node + master IPs) are reserved automatically
-if the cluster's data network itself is placed under pool management.
+Cluster IP addresses (node + master IPs) are reserved automatically
+as external if the cluster's data network itself is placed under
+pool management.
Helper ConfigWriter methods provide free IP address generation and
reservation, using a TemporaryReservationManager.
We also introduce a new ``ip`` address value, ``constants.NIC_IP_POOL``,
that specifies that a given NIC's IP address should be obtained using
-the IP address pool of the specified network. This value is only valid
+the first available IP address inside the pool of the specified network.
+(reservations OR external_reservations). This value is only valid
for NICs belonging to a network. A NIC's IP address can also be
specified manually, as long as it is contained in the network the NIC
-is connected to.
+is connected to. In case this IP is externally reserved, Ganeti will produce
+an error which the user can override if explicitly requested. Of course
+this IP will be reserved and will not be able to be assigned to another
+instance.
Hooks
self.LogInfo("Chose IP %s from network %s", nic.ip, nobj.name)
else:
try:
- self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId())
+ self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId(),
+ check=self.op.conflicts_check)
except errors.ReservationError:
raise errors.OpPrereqError("IP address %s already in use"
" or does not belong to network %s" %
# Reserve new IP if in the new network if any
elif new_net_uuid:
try:
- self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
+ self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId(),
+ check=self.op.conflicts_check)
self.LogInfo("Reserving IP %s in network %s",
new_ip, new_net_obj.name)
except errors.ReservationError:
if self.op.add_reserved_ips:
for ip in self.op.add_reserved_ips:
try:
- if self.pool.IsReserved(ip):
- self.LogWarning("IP address %s is already reserved", ip)
- else:
- self.pool.Reserve(ip, external=True)
+ self.pool.Reserve(ip, external=True)
except errors.AddressPoolError, err:
self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
self.LogWarning("Cannot unreserve Gateway's IP")
continue
try:
- if not self.pool.IsReserved(ip):
- self.LogWarning("IP address %s is already unreserved", ip)
- else:
- self.pool.Release(ip, external=True)
+ self.pool.Release(ip, external=True)
except errors.AddressPoolError, err:
self.LogWarning("Cannot release IP address %s: %s", ip, err)
_, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
return address
- def _UnlockedReserveIp(self, net_uuid, address, ec_id):
+ def _UnlockedReserveIp(self, net_uuid, address, ec_id, check=True):
"""Reserve a given IPv4 address for use by an instance.
"""
pool = network.AddressPool(nobj)
try:
isreserved = pool.IsReserved(address)
+ isextreserved = pool.IsReserved(address, external=True)
except errors.AddressPoolError:
raise errors.ReservationError("IP address not in network")
if isreserved:
raise errors.ReservationError("IP address already in use")
+ if check and isextreserved:
+ raise errors.ReservationError("IP is externally reserved")
return self._temporary_ips.Reserve(ec_id,
(constants.RESERVE_ACTION,
address, net_uuid))
@locking.ssynchronized(_config_lock, shared=1)
- def ReserveIp(self, net_uuid, address, ec_id):
+ def ReserveIp(self, net_uuid, address, ec_id, check=True):
"""Reserve a given IPv4 address for use by an instance.
"""
if net_uuid:
- return self._UnlockedReserveIp(net_uuid, address, ec_id)
+ return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
@locking.ssynchronized(_config_lock, shared=1)
def ReserveLV(self, lv_name, ec_id):
def Validate(self):
assert len(self.reservations) == self._GetSize()
assert len(self.ext_reservations) == self._GetSize()
- all_res = self.reservations & self.ext_reservations
- assert not all_res.any()
if self.gateway is not None:
assert self.gateway in self.network
"""
return self.all_reservations.to01().replace("1", "X").replace("0", ".")
- def IsReserved(self, address):
+ def IsReserved(self, address, external=False):
"""Checks if the given IP is reserved.
"""
idx = self._GetAddrIndex(address)
- return self.all_reservations[idx]
+ if external:
+ return self.ext_reservations[idx]
+ else:
+ return self.reservations[idx]
def Reserve(self, address, external=False):
"""Mark an address as used.
"""
- if self.IsReserved(address):
- raise errors.AddressPoolError("%s is already reserved" % address)
+ if self.IsReserved(address, external):
+ if external:
+ msg = "IP %s is already externally reserved" % address
+ else:
+ msg = "IP %s is already used by an instance" % address
+ raise errors.AddressPoolError(msg)
+
self._Mark(address, external=external)
def Release(self, address, external=False):
"""Release a given address reservation.
"""
+ if not self.IsReserved(address, external):
+ if external:
+ msg = "IP %s is not externally reserved" % address
+ else:
+ msg = "IP %s is not used by an instance" % address
+ raise errors.AddressPoolError(msg)
+
self._Mark(address, value=False, external=external)
def GetFreeAddress(self):
connected to the said network. ``--no-conflicts-check`` can be used
to override this check. The special value **pool** causes Ganeti to
select an IP from the the network the NIC is or will be connected to.
+ One can pick an externally reserved IP of a network along with
+ ``--no-conflict-check``. Note that this IP cannot be assigned to
+ any other instance until it gets released.
mode
specifies the connection mode for this NIC: routed, bridged or