Commit 6c0a75db authored by Dimitris Aragiorgis's avatar Dimitris Aragiorgis Committed by Iustin Pop
Browse files

Basic IP pool management logic



Implement LUs for corresponding opcodes:
 * LUNetworkAdd:
   - Check for IP validity
   - Reserves all necessary IPs
   - Create new Network config object
 * LUNetworkRemove:
   - Checks if connected to any nodegroup
   - Remove a Network config object

Implement basic config methods:
 * LookupNetwork()
   - Given the network name return the network UUID
 * AddNetwork()
   - Add a new network to the config
 * RemoveNetwork()
   - Remove a network from the config

Add new locking level: LEVEL_NETWORK

Add various useful config methods for retrieving network info.
Signed-off-by: default avatarApollon Oikonomopoulos <apollon@noc.grnet.gr>
Signed-off-by: default avatarDimitris Aragiorgis <dimara@grnet.gr>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 1de1cf25
......@@ -40,6 +40,7 @@ import tempfile
import shutil
import itertools
import operator
import ipaddr
 
from ganeti import ssh
from ganeti import utils
......@@ -61,6 +62,7 @@ from ganeti import rpc
from ganeti import runtime
from ganeti import pathutils
from ganeti import vcluster
from ganeti import network
from ganeti.masterd import iallocator
 
import ganeti.masterd.instance # pylint: disable=W0611
......@@ -15303,19 +15305,169 @@ class LUTestAllocator(NoHooksLU):
 
# Network LUs
class LUNetworkAdd(LogicalUnit):
"""Logical unit for creating networks.
"""
HPATH = "network-add"
HTYPE = constants.HTYPE_NETWORK
REQ_BGL = False
def BuildHooksNodes(self):
pass
"""Build hooks nodes.
"""
mn = self.cfg.GetMasterNode()
return ([mn], [mn])
def ExpandNames(self):
self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
self.needed_locks = {}
self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid
def CheckPrereq(self):
"""Check prerequisites.
This checks that the given group name is not an existing node group
already.
"""
if self.op.network is None:
raise errors.OpPrereqError("Network must be given",
errors.ECODE_INVAL)
uuid = self.cfg.LookupNetwork(self.op.network_name)
if uuid:
raise errors.OpPrereqError("Network '%s' already defined" %
self.op.network, errors.ECODE_EXISTS)
 
def BuildHooksEnv(self):
pass
"""Build hooks env.
"""
env = {
"NETWORK_NAME": self.op.network_name,
"NETWORK_SUBNET": self.op.network,
"NETWORK_GATEWAY": self.op.gateway,
"NETWORK_SUBNET6": self.op.network6,
"NETWORK_GATEWAY6": self.op.gateway6,
"NETWORK_MAC_PREFIX": self.op.mac_prefix,
"NETWORK_TYPE": self.op.network_type,
}
return env
def Exec(self, feedback_fn):
"""Add the ip pool to the cluster.
"""
nobj = objects.Network(name=self.op.network_name,
network=self.op.network,
gateway=self.op.gateway,
network6=self.op.network6,
gateway6=self.op.gateway6,
mac_prefix=self.op.mac_prefix,
network_type=self.op.network_type,
uuid=self.network_uuid,
family=4)
# Initialize the associated address pool
try:
pool = network.AddressPool.InitializeNetwork(nobj)
except errors.AddressPoolError, e:
raise errors.OpExecError("Cannot create IP pool for this network. %s" % e)
# Check if we need to reserve the nodes and the cluster master IP
# These may not be allocated to any instances in routed mode, as
# they wouldn't function anyway.
for node in self.cfg.GetAllNodesInfo().values():
for ip in [node.primary_ip, node.secondary_ip]:
try:
pool.Reserve(ip)
self.LogInfo("Reserved node %s's IP (%s)", node.name, ip)
except errors.AddressPoolError:
pass
master_ip = self.cfg.GetClusterInfo().master_ip
try:
pool.Reserve(master_ip)
self.LogInfo("Reserved cluster master IP (%s)", master_ip)
except errors.AddressPoolError:
pass
if self.op.add_reserved_ips:
for ip in self.op.add_reserved_ips:
try:
pool.Reserve(ip, external=True)
except errors.AddressPoolError, e:
raise errors.OpExecError("Cannot reserve IP %s. %s " % (ip, e))
self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
del self.remove_locks[locking.LEVEL_NETWORK]
 
 
class LUNetworkRemove(LogicalUnit):
def BuildHooksNodes(self):
pass
HPATH = "network-remove"
HTYPE = constants.HTYPE_NETWORK
REQ_BGL = False
def ExpandNames(self):
self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
self.needed_locks = {
locking.LEVEL_NETWORK: [self.network_uuid],
}
def CheckPrereq(self):
"""Check prerequisites.
This checks that the given network name exists as a network, that is
empty (i.e., contains no nodes), and that is not the last group of the
cluster.
"""
if not self.network_uuid:
raise errors.OpPrereqError("Network %s not found" % self.op.network_name,
errors.ECODE_INVAL)
# Verify that the network is not conncted.
node_groups = [group.name
for group in self.cfg.GetAllNodeGroupsInfo().values()
for network in group.networks.keys()
if network == self.network_uuid]
if node_groups:
self.LogWarning("Nework '%s' is connected to the following"
" node groups: %s" % (self.op.network_name,
utils.CommaJoin(utils.NiceSort(node_groups))))
raise errors.OpPrereqError("Network still connected",
errors.ECODE_STATE)
 
def BuildHooksEnv(self):
pass
"""Build hooks env.
"""
return {
"NETWORK_NAME": self.op.network_name,
}
def BuildHooksNodes(self):
"""Build hooks nodes.
"""
mn = self.cfg.GetMasterNode()
return ([mn], [mn])
def Exec(self, feedback_fn):
"""Remove the network.
"""
try:
self.cfg.RemoveNetwork(self.network_uuid)
except errors.ConfigurationError:
raise errors.OpExecError("Network '%s' with UUID %s disappeared" %
(self.op.network_name, self.network_uuid))
 
 
class LUNetworkSetParams(LogicalUnit):
......
......@@ -51,6 +51,7 @@ from ganeti import uidpool
from ganeti import netutils
from ganeti import runtime
from ganeti import pathutils
from ganeti import network
_config_lock = locking.SharedLock("ConfigWriter")
......@@ -2094,6 +2095,9 @@ class ConfigWriter:
nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
self._config_data.nodegroups.values()]
nodegroups_data = fn(utils.NiceSort(nodegroups))
networks = ["%s %s" % (net.uuid, net.name) for net in
self._config_data.networks.values()]
networks_data = fn(utils.NiceSort(networks))
ssconf_values = {
constants.SS_CLUSTER_NAME: cluster.cluster_name,
......@@ -2118,6 +2122,7 @@ class ConfigWriter:
constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
constants.SS_UID_POOL: uid_pool,
constants.SS_NODEGROUPS: nodegroups_data,
constants.SS_NETWORKS: networks_data,
}
bad_values = [(k, v) for k, v in ssconf_values.items()
if not isinstance(v, (str, basestring))]
......@@ -2245,3 +2250,141 @@ class ConfigWriter:
"""
for rm in self._all_rms:
rm.DropECReservations(ec_id)
@locking.ssynchronized(_config_lock, shared=1)
def GetAllNetworksInfo(self):
"""Get the configuration of all networks
"""
return dict(self._config_data.networks)
def _UnlockedGetNetworkList(self):
"""Get the list of networks.
This function is for internal use, when the config lock is already held.
"""
return self._config_data.networks.keys()
@locking.ssynchronized(_config_lock, shared=1)
def GetNetworkList(self):
"""Get the list of networks.
@return: array of networks, ex. ["main", "vlan100", "200]
"""
return self._UnlockedGetNetworkList()
@locking.ssynchronized(_config_lock, shared=1)
def GetNetworkNames(self):
"""Get a list of network names
"""
names = [network.name
for network in self._config_data.networks.values()]
return names
def _UnlockedGetNetwork(self, uuid):
"""Returns information about a network.
This function is for internal use, when the config lock is already held.
"""
if uuid not in self._config_data.networks:
return None
return self._config_data.networks[uuid]
@locking.ssynchronized(_config_lock, shared=1)
def GetNetwork(self, uuid):
"""Returns information about a network.
It takes the information from the configuration file.
@param uuid: UUID of the network
@rtype: L{objects.Network}
@return: the network object
"""
return self._UnlockedGetNetwork(uuid)
@locking.ssynchronized(_config_lock)
def AddNetwork(self, net, ec_id, check_uuid=True):
"""Add a network to the configuration.
@type net: L{objects.Network}
@param net: the Network object to add
@type ec_id: string
@param ec_id: unique id for the job to use when creating a missing UUID
"""
self._UnlockedAddNetwork(net, ec_id, check_uuid)
self._WriteConfig()
def _UnlockedAddNetwork(self, net, ec_id, check_uuid):
"""Add a network to the configuration.
"""
logging.info("Adding network %s to configuration", net.name)
if check_uuid:
self._EnsureUUID(net, ec_id)
existing_uuid = self._UnlockedLookupNetwork(net.name)
if existing_uuid:
raise errors.OpPrereqError("Desired network name '%s' already"
" exists as a network (UUID: %s)" %
(net.name, existing_uuid),
errors.ECODE_EXISTS)
net.serial_no = 1
self._config_data.networks[net.uuid] = net
self._config_data.cluster.serial_no += 1
def _UnlockedLookupNetwork(self, target):
"""Lookup a network's UUID.
@type target: string
@param target: network name or UUID
@rtype: string
@return: network UUID
@raises errors.OpPrereqError: when the target network cannot be found
"""
if target in self._config_data.networks:
return target
for net in self._config_data.networks.values():
if net.name == target:
return net.uuid
return None
@locking.ssynchronized(_config_lock, shared=1)
def LookupNetwork(self, target):
"""Lookup a network's UUID.
This function is just a wrapper over L{_UnlockedLookupNetwork}.
@type target: string
@param target: network name or UUID
@rtype: string
@return: network UUID
"""
return self._UnlockedLookupNetwork(target)
@locking.ssynchronized(_config_lock)
def RemoveNetwork(self, network_uuid):
"""Remove a network from the configuration.
@type network_uuid: string
@param network_uuid: the UUID of the network to remove
"""
logging.info("Removing network %s from configuration", network_uuid)
if network_uuid not in self._config_data.networks:
raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
del self._config_data.networks[network_uuid]
self._config_data.cluster.serial_no += 1
self._WriteConfig()
......@@ -1477,6 +1477,7 @@ LEVEL_NODE = 3
#: Level for node resources, used for operations with possibly high impact on
#: the node's disks.
LEVEL_NODE_RES = 4
LEVEL_NETWORK = 5
LEVELS = [
LEVEL_CLUSTER,
......@@ -1484,6 +1485,7 @@ LEVELS = [
LEVEL_NODEGROUP,
LEVEL_NODE,
LEVEL_NODE_RES,
LEVEL_NETWORK,
]
# Lock levels which are modifiable
......@@ -1492,6 +1494,7 @@ LEVELS_MOD = frozenset([
LEVEL_NODE,
LEVEL_NODEGROUP,
LEVEL_INSTANCE,
LEVEL_NETWORK,
])
#: Lock level names (make sure to use singular form)
......@@ -1501,6 +1504,7 @@ LEVEL_NAMES = {
LEVEL_NODEGROUP: "nodegroup",
LEVEL_NODE: "node",
LEVEL_NODE_RES: "node-res",
LEVEL_NETWORK: "network",
}
# Constant for the big ganeti lock
......@@ -1518,7 +1522,7 @@ class GanetiLockManager:
"""
_instance = None
def __init__(self, nodes, nodegroups, instances):
def __init__(self, nodes, nodegroups, instances, networks):
"""Constructs a new GanetiLockManager object.
There should be only a GanetiLockManager object at any time, so this
......@@ -1543,8 +1547,8 @@ class GanetiLockManager:
LEVEL_NODE: LockSet(nodes, "node", monitor=self._monitor),
LEVEL_NODE_RES: LockSet(nodes, "node-res", monitor=self._monitor),
LEVEL_NODEGROUP: LockSet(nodegroups, "nodegroup", monitor=self._monitor),
LEVEL_INSTANCE: LockSet(instances, "instance",
monitor=self._monitor),
LEVEL_INSTANCE: LockSet(instances, "instance", monitor=self._monitor),
LEVEL_NETWORK: LockSet(networks, "network", monitor=self._monitor),
}
assert compat.all(ls.name == LEVEL_NAMES[level]
......
......@@ -487,7 +487,8 @@ class GanetiContext(object):
self.glm = locking.GanetiLockManager(
self.cfg.GetNodeList(),
self.cfg.GetNodeGroupList(),
self.cfg.GetInstanceList())
self.cfg.GetInstanceList(),
self.cfg.GetNetworkList())
self.cfg.SetContext(self)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment