4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Logical units dealing with networks."""
24 from ganeti import constants
25 from ganeti import errors
26 from ganeti import locking
27 from ganeti import network
28 from ganeti import objects
29 from ganeti import qlang
30 from ganeti import query
31 from ganeti import utils
32 from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase
33 from ganeti.cmdlib.common import ShareAll
36 def _BuildNetworkHookEnv(name, subnet, gateway, network6, gateway6,
38 """Builds network related env variables for hooks
40 This builds the hook environment from individual variables.
43 @param name: the name of the network
45 @param subnet: the ipv4 subnet
47 @param gateway: the ipv4 gateway
48 @type network6: string
49 @param network6: the ipv6 subnet
50 @type gateway6: string
51 @param gateway6: the ipv6 gateway
52 @type mac_prefix: string
53 @param mac_prefix: the mac_prefix
55 @param tags: the tags of the network
60 env["NETWORK_NAME"] = name
62 env["NETWORK_SUBNET"] = subnet
64 env["NETWORK_GATEWAY"] = gateway
66 env["NETWORK_SUBNET6"] = network6
68 env["NETWORK_GATEWAY6"] = gateway6
70 env["NETWORK_MAC_PREFIX"] = mac_prefix
72 env["NETWORK_TAGS"] = " ".join(tags)
77 class LUNetworkAdd(LogicalUnit):
78 """Logical unit for creating networks.
82 HTYPE = constants.HTYPE_NETWORK
85 def BuildHooksNodes(self):
89 mn = self.cfg.GetMasterNode()
92 def CheckArguments(self):
93 if self.op.mac_prefix:
94 self.op.mac_prefix = \
95 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
97 def ExpandNames(self):
98 self.network_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
100 if self.op.conflicts_check:
101 self.share_locks[locking.LEVEL_NODE] = 1
102 self.share_locks[locking.LEVEL_NODE_ALLOC] = 1
103 self.needed_locks = {
104 locking.LEVEL_NODE: locking.ALL_SET,
105 locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
108 self.needed_locks = {}
110 self.add_locks[locking.LEVEL_NETWORK] = self.network_uuid
112 def CheckPrereq(self):
113 if self.op.network is None:
114 raise errors.OpPrereqError("Network must be given",
118 existing_uuid = self.cfg.LookupNetwork(self.op.network_name)
119 except errors.OpPrereqError:
122 raise errors.OpPrereqError("Desired network name '%s' already exists as a"
123 " network (UUID: %s)" %
124 (self.op.network_name, existing_uuid),
128 for tag in self.op.tags:
129 objects.TaggableObject.ValidateTag(tag)
131 def BuildHooksEnv(self):
136 "name": self.op.network_name,
137 "subnet": self.op.network,
138 "gateway": self.op.gateway,
139 "network6": self.op.network6,
140 "gateway6": self.op.gateway6,
141 "mac_prefix": self.op.mac_prefix,
142 "tags": self.op.tags,
144 return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
146 def Exec(self, feedback_fn):
147 """Add the ip pool to the cluster.
150 nobj = objects.Network(name=self.op.network_name,
151 network=self.op.network,
152 gateway=self.op.gateway,
153 network6=self.op.network6,
154 gateway6=self.op.gateway6,
155 mac_prefix=self.op.mac_prefix,
156 uuid=self.network_uuid)
157 # Initialize the associated address pool
159 pool = network.AddressPool.InitializeNetwork(nobj)
160 except errors.AddressPoolError, err:
161 raise errors.OpExecError("Cannot create IP address pool for network"
162 " '%s': %s" % (self.op.network_name, err))
164 # Check if we need to reserve the nodes and the cluster master IP
165 # These may not be allocated to any instances in routed mode, as
166 # they wouldn't function anyway.
167 if self.op.conflicts_check:
168 for node in self.cfg.GetAllNodesInfo().values():
169 for ip in [node.primary_ip, node.secondary_ip]:
171 if pool.Contains(ip):
172 pool.Reserve(ip, external=True)
173 self.LogInfo("Reserved IP address of node '%s' (%s)",
175 except errors.AddressPoolError, err:
176 self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
179 master_ip = self.cfg.GetClusterInfo().master_ip
181 if pool.Contains(master_ip):
182 pool.Reserve(master_ip, external=True)
183 self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
184 except errors.AddressPoolError, err:
185 self.LogWarning("Cannot reserve cluster master IP address (%s): %s",
188 if self.op.add_reserved_ips:
189 for ip in self.op.add_reserved_ips:
191 pool.Reserve(ip, external=True)
192 except errors.AddressPoolError, err:
193 raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
197 for tag in self.op.tags:
200 self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
201 del self.remove_locks[locking.LEVEL_NETWORK]
204 class LUNetworkRemove(LogicalUnit):
205 HPATH = "network-remove"
206 HTYPE = constants.HTYPE_NETWORK
209 def ExpandNames(self):
210 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
212 self.share_locks[locking.LEVEL_NODEGROUP] = 1
213 self.needed_locks = {
214 locking.LEVEL_NETWORK: [self.network_uuid],
215 locking.LEVEL_NODEGROUP: locking.ALL_SET,
218 def CheckPrereq(self):
219 """Check prerequisites.
221 This checks that the given network name exists as a network, that is
222 empty (i.e., contains no nodes), and that is not the last group of the
226 # Verify that the network is not conncted.
227 node_groups = [group.name
228 for group in self.cfg.GetAllNodeGroupsInfo().values()
229 if self.network_uuid in group.networks]
232 self.LogWarning("Network '%s' is connected to the following"
234 (self.op.network_name,
235 utils.CommaJoin(utils.NiceSort(node_groups))))
236 raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
238 def BuildHooksEnv(self):
243 "NETWORK_NAME": self.op.network_name,
246 def BuildHooksNodes(self):
247 """Build hooks nodes.
250 mn = self.cfg.GetMasterNode()
253 def Exec(self, feedback_fn):
254 """Remove the network.
258 self.cfg.RemoveNetwork(self.network_uuid)
259 except errors.ConfigurationError:
260 raise errors.OpExecError("Network '%s' with UUID %s disappeared" %
261 (self.op.network_name, self.network_uuid))
264 class LUNetworkSetParams(LogicalUnit):
265 """Modifies the parameters of a network.
268 HPATH = "network-modify"
269 HTYPE = constants.HTYPE_NETWORK
272 def CheckArguments(self):
273 if (self.op.gateway and
274 (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
275 raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
276 " at once", errors.ECODE_INVAL)
278 def ExpandNames(self):
279 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
281 self.needed_locks = {
282 locking.LEVEL_NETWORK: [self.network_uuid],
285 def CheckPrereq(self):
286 """Check prerequisites.
289 self.network = self.cfg.GetNetwork(self.network_uuid)
290 self.gateway = self.network.gateway
291 self.mac_prefix = self.network.mac_prefix
292 self.network6 = self.network.network6
293 self.gateway6 = self.network.gateway6
294 self.tags = self.network.tags
296 self.pool = network.AddressPool(self.network)
299 if self.op.gateway == constants.VALUE_NONE:
302 self.gateway = self.op.gateway
303 if self.pool.IsReserved(self.gateway):
304 raise errors.OpPrereqError("Gateway IP address '%s' is already"
305 " reserved" % self.gateway,
308 if self.op.mac_prefix:
309 if self.op.mac_prefix == constants.VALUE_NONE:
310 self.mac_prefix = None
313 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
316 if self.op.gateway6 == constants.VALUE_NONE:
319 self.gateway6 = self.op.gateway6
322 if self.op.network6 == constants.VALUE_NONE:
325 self.network6 = self.op.network6
327 def BuildHooksEnv(self):
332 "name": self.op.network_name,
333 "subnet": self.network.network,
334 "gateway": self.gateway,
335 "network6": self.network6,
336 "gateway6": self.gateway6,
337 "mac_prefix": self.mac_prefix,
340 return _BuildNetworkHookEnv(**args) # pylint: disable=W0142
342 def BuildHooksNodes(self):
343 """Build hooks nodes.
346 mn = self.cfg.GetMasterNode()
349 def Exec(self, feedback_fn):
350 """Modifies the network.
353 #TODO: reserve/release via temporary reservation manager
354 # extend cfg.ReserveIp/ReleaseIp with the external flag
356 if self.gateway == self.network.gateway:
357 self.LogWarning("Gateway is already %s", self.gateway)
360 self.pool.Reserve(self.gateway, external=True)
361 if self.network.gateway:
362 self.pool.Release(self.network.gateway, external=True)
363 self.network.gateway = self.gateway
365 if self.op.add_reserved_ips:
366 for ip in self.op.add_reserved_ips:
368 if self.pool.IsReserved(ip):
369 self.LogWarning("IP address %s is already reserved", ip)
371 self.pool.Reserve(ip, external=True)
372 except errors.AddressPoolError, err:
373 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
375 if self.op.remove_reserved_ips:
376 for ip in self.op.remove_reserved_ips:
377 if ip == self.network.gateway:
378 self.LogWarning("Cannot unreserve Gateway's IP")
381 if not self.pool.IsReserved(ip):
382 self.LogWarning("IP address %s is already unreserved", ip)
384 self.pool.Release(ip, external=True)
385 except errors.AddressPoolError, err:
386 self.LogWarning("Cannot release IP address %s: %s", ip, err)
388 if self.op.mac_prefix:
389 self.network.mac_prefix = self.mac_prefix
392 self.network.network6 = self.network6
395 self.network.gateway6 = self.gateway6
399 self.cfg.Update(self.network, feedback_fn)
402 class NetworkQuery(QueryBase):
403 FIELDS = query.NETWORK_FIELDS
405 def ExpandNames(self, lu):
407 lu.share_locks = ShareAll()
409 self.do_locking = self.use_locking
411 all_networks = lu.cfg.GetAllNetworksInfo()
412 name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
418 for name in self.names:
419 if name in name_to_uuid:
420 self.wanted.append(name_to_uuid[name])
425 raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
428 self.wanted = locking.ALL_SET
431 lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted
432 if query.NETQ_INST in self.requested_data:
433 lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
434 if query.NETQ_GROUP in self.requested_data:
435 lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
437 def DeclareLocks(self, lu, level):
440 def _GetQueryData(self, lu):
441 """Computes the list of networks and their attributes.
444 all_networks = lu.cfg.GetAllNetworksInfo()
446 network_uuids = self._GetNames(lu, all_networks.keys(),
447 locking.LEVEL_NETWORK)
449 do_instances = query.NETQ_INST in self.requested_data
450 do_groups = query.NETQ_GROUP in self.requested_data
452 network_to_instances = None
453 network_to_groups = None
455 # For NETQ_GROUP, we need to map network->[groups]
457 all_groups = lu.cfg.GetAllNodeGroupsInfo()
458 network_to_groups = dict((uuid, []) for uuid in network_uuids)
459 for _, group in all_groups.iteritems():
460 for net_uuid in network_uuids:
461 netparams = group.networks.get(net_uuid, None)
463 info = (group.name, netparams[constants.NIC_MODE],
464 netparams[constants.NIC_LINK])
466 network_to_groups[net_uuid].append(info)
469 all_instances = lu.cfg.GetAllInstancesInfo()
470 network_to_instances = dict((uuid, []) for uuid in network_uuids)
471 for instance in all_instances.values():
472 for nic in instance.nics:
473 if nic.network in network_uuids:
474 network_to_instances[nic.network].append(instance.name)
477 if query.NETQ_STATS in self.requested_data:
480 self._GetStats(network.AddressPool(all_networks[uuid])))
481 for uuid in network_uuids)
485 return query.NetworkQueryData([all_networks[uuid]
486 for uuid in network_uuids],
488 network_to_instances,
493 """Returns statistics for a network address pool.
497 "free_count": pool.GetFreeCount(),
498 "reserved_count": pool.GetReservedCount(),
499 "map": pool.GetMap(),
500 "external_reservations":
501 utils.CommaJoin(pool.GetExternalReservations()),
505 class LUNetworkQuery(NoHooksLU):
506 """Logical unit for querying networks.
511 def CheckArguments(self):
512 self.nq = NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names),
513 self.op.output_fields, self.op.use_locking)
515 def ExpandNames(self):
516 self.nq.ExpandNames(self)
518 def Exec(self, feedback_fn):
519 return self.nq.OldStyleQuery(self)
522 def _FmtNetworkConflict(details):
523 """Utility for L{_NetworkConflictCheck}.
526 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
527 for (idx, ipaddr) in details)
530 def _NetworkConflictCheck(lu, check_fn, action, instances):
531 """Checks for network interface conflicts with a network.
533 @type lu: L{LogicalUnit}
534 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
536 @param check_fn: Function checking for conflict
538 @param action: Part of error message (see code)
539 @raise errors.OpPrereqError: If conflicting IP addresses are found.
544 for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
545 instconflicts = [(idx, nic.ip)
546 for (idx, nic) in enumerate(instance.nics)
550 conflicts.append((instance.name, instconflicts))
553 lu.LogWarning("IP addresses from network '%s', which is about to %s"
554 " node group '%s', are in use: %s" %
555 (lu.network_name, action, lu.group.name,
556 utils.CommaJoin(("%s: %s" %
557 (name, _FmtNetworkConflict(details)))
558 for (name, details) in conflicts)))
560 raise errors.OpPrereqError("Conflicting IP addresses found; "
561 " remove/modify the corresponding network"
562 " interfaces", errors.ECODE_STATE)
565 class LUNetworkConnect(LogicalUnit):
566 """Connect a network to a nodegroup
569 HPATH = "network-connect"
570 HTYPE = constants.HTYPE_NETWORK
573 def ExpandNames(self):
574 self.network_name = self.op.network_name
575 self.group_name = self.op.group_name
576 self.network_mode = self.op.network_mode
577 self.network_link = self.op.network_link
579 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
580 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
582 self.needed_locks = {
583 locking.LEVEL_NODEGROUP: [self.group_uuid],
586 if self.op.conflicts_check:
587 self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
588 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
589 self.share_locks[locking.LEVEL_NETWORK] = 1
590 self.share_locks[locking.LEVEL_INSTANCE] = 1
592 def DeclareLocks(self, level):
595 def BuildHooksEnv(self):
597 "GROUP_NAME": self.group_name,
598 "GROUP_NETWORK_MODE": self.network_mode,
599 "GROUP_NETWORK_LINK": self.network_link,
603 def BuildHooksNodes(self):
604 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
605 return (nodes, nodes)
607 def CheckPrereq(self):
608 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
610 assert self.group_uuid in owned_groups
612 # Check if locked instances are still correct
613 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
616 constants.NIC_MODE: self.network_mode,
617 constants.NIC_LINK: self.network_link,
619 objects.NIC.CheckParameterSyntax(self.netparams)
621 self.group = self.cfg.GetNodeGroup(self.group_uuid)
622 #if self.network_mode == constants.NIC_MODE_BRIDGED:
623 # _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
624 self.connected = False
625 if self.network_uuid in self.group.networks:
626 self.LogWarning("Network '%s' is already mapped to group '%s'" %
627 (self.network_name, self.group.name))
628 self.connected = True
630 # check only if not already connected
631 elif self.op.conflicts_check:
632 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
634 _NetworkConflictCheck(self, lambda nic: pool.Contains(nic.ip),
635 "connect to", owned_instances)
637 def Exec(self, feedback_fn):
638 # Connect the network and update the group only if not already connected
639 if not self.connected:
640 self.group.networks[self.network_uuid] = self.netparams
641 self.cfg.Update(self.group, feedback_fn)
644 class LUNetworkDisconnect(LogicalUnit):
645 """Disconnect a network to a nodegroup
648 HPATH = "network-disconnect"
649 HTYPE = constants.HTYPE_NETWORK
652 def ExpandNames(self):
653 self.network_name = self.op.network_name
654 self.group_name = self.op.group_name
656 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
657 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
659 self.needed_locks = {
660 locking.LEVEL_INSTANCE: locking.ALL_SET,
661 locking.LEVEL_NODEGROUP: [self.group_uuid],
663 self.share_locks[locking.LEVEL_INSTANCE] = 1
665 def DeclareLocks(self, level):
668 def BuildHooksEnv(self):
670 "GROUP_NAME": self.group_name,
674 def BuildHooksNodes(self):
675 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
676 return (nodes, nodes)
678 def CheckPrereq(self):
679 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
681 assert self.group_uuid in owned_groups
683 # Check if locked instances are still correct
684 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
686 self.group = self.cfg.GetNodeGroup(self.group_uuid)
687 self.connected = True
688 if self.network_uuid not in self.group.networks:
689 self.LogWarning("Network '%s' is not mapped to group '%s'",
690 self.network_name, self.group.name)
691 self.connected = False
693 # We need this check only if network is not already connected
695 _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
696 "disconnect from", owned_instances)
698 def Exec(self, feedback_fn):
699 # Disconnect the network and update the group only if network is connected
701 del self.group.networks[self.network_uuid]
702 self.cfg.Update(self.group, feedback_fn)