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, CheckNodeGroupInstances
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):
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)
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.uuid)
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 @param instances: the instances to check
540 @type instances: list of instance objects
541 @raise errors.OpPrereqError: If conflicting IP addresses are found.
546 for instance in instances:
547 instconflicts = [(idx, nic.ip)
548 for (idx, nic) in enumerate(instance.nics)
552 conflicts.append((instance.name, instconflicts))
555 lu.LogWarning("IP addresses from network '%s', which is about to %s"
556 " node group '%s', are in use: %s" %
557 (lu.network_name, action, lu.group.name,
558 utils.CommaJoin(("%s: %s" %
559 (name, _FmtNetworkConflict(details)))
560 for (name, details) in conflicts)))
562 raise errors.OpPrereqError("Conflicting IP addresses found; "
563 " remove/modify the corresponding network"
564 " interfaces", errors.ECODE_STATE)
567 class LUNetworkConnect(LogicalUnit):
568 """Connect a network to a nodegroup
571 HPATH = "network-connect"
572 HTYPE = constants.HTYPE_NETWORK
575 def ExpandNames(self):
576 self.network_name = self.op.network_name
577 self.group_name = self.op.group_name
578 self.network_mode = self.op.network_mode
579 self.network_link = self.op.network_link
581 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
582 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
584 self.needed_locks = {
585 locking.LEVEL_INSTANCE: [],
586 locking.LEVEL_NODEGROUP: [self.group_uuid],
588 self.share_locks[locking.LEVEL_INSTANCE] = 1
590 if self.op.conflicts_check:
591 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
592 self.share_locks[locking.LEVEL_NETWORK] = 1
594 def DeclareLocks(self, level):
595 if level == locking.LEVEL_INSTANCE:
596 assert not self.needed_locks[locking.LEVEL_INSTANCE]
598 # Lock instances optimistically, needs verification once group lock has
600 if self.op.conflicts_check:
601 self.needed_locks[locking.LEVEL_INSTANCE] = \
602 self.cfg.GetInstanceNames(
603 self.cfg.GetNodeGroupInstances(self.group_uuid))
605 def BuildHooksEnv(self):
607 "GROUP_NAME": self.group_name,
608 "GROUP_NETWORK_MODE": self.network_mode,
609 "GROUP_NETWORK_LINK": self.network_link,
613 def BuildHooksNodes(self):
614 node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
615 return (node_uuids, node_uuids)
617 def CheckPrereq(self):
618 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
620 assert self.group_uuid in owned_groups
622 # Check if locked instances are still correct
623 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
624 if self.op.conflicts_check:
625 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
628 constants.NIC_MODE: self.network_mode,
629 constants.NIC_LINK: self.network_link,
631 objects.NIC.CheckParameterSyntax(self.netparams)
633 self.group = self.cfg.GetNodeGroup(self.group_uuid)
634 #if self.network_mode == constants.NIC_MODE_BRIDGED:
635 # _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
636 self.connected = False
637 if self.network_uuid in self.group.networks:
638 self.LogWarning("Network '%s' is already mapped to group '%s'" %
639 (self.network_name, self.group.name))
640 self.connected = True
642 # check only if not already connected
643 elif self.op.conflicts_check:
644 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
646 _NetworkConflictCheck(
647 self, lambda nic: pool.Contains(nic.ip), "connect to",
648 [instance_info for (_, instance_info) in
649 self.cfg.GetMultiInstanceInfoByName(owned_instance_names)])
651 def Exec(self, feedback_fn):
652 # Connect the network and update the group only if not already connected
653 if not self.connected:
654 self.group.networks[self.network_uuid] = self.netparams
655 self.cfg.Update(self.group, feedback_fn)
658 class LUNetworkDisconnect(LogicalUnit):
659 """Disconnect a network to a nodegroup
662 HPATH = "network-disconnect"
663 HTYPE = constants.HTYPE_NETWORK
666 def ExpandNames(self):
667 self.network_name = self.op.network_name
668 self.group_name = self.op.group_name
670 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
671 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
673 self.needed_locks = {
674 locking.LEVEL_INSTANCE: [],
675 locking.LEVEL_NODEGROUP: [self.group_uuid],
677 self.share_locks[locking.LEVEL_INSTANCE] = 1
679 def DeclareLocks(self, level):
680 if level == locking.LEVEL_INSTANCE:
681 assert not self.needed_locks[locking.LEVEL_INSTANCE]
683 # Lock instances optimistically, needs verification once group lock has
685 self.needed_locks[locking.LEVEL_INSTANCE] = \
686 self.cfg.GetInstanceNames(
687 self.cfg.GetNodeGroupInstances(self.group_uuid))
689 def BuildHooksEnv(self):
691 "GROUP_NAME": self.group_name,
695 def BuildHooksNodes(self):
696 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
697 return (nodes, nodes)
699 def CheckPrereq(self):
700 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
702 assert self.group_uuid in owned_groups
704 # Check if locked instances are still correct
705 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
706 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
708 self.group = self.cfg.GetNodeGroup(self.group_uuid)
709 self.connected = True
710 if self.network_uuid not in self.group.networks:
711 self.LogWarning("Network '%s' is not mapped to group '%s'",
712 self.network_name, self.group.name)
713 self.connected = False
715 # We need this check only if network is not already connected
717 _NetworkConflictCheck(
718 self, lambda nic: nic.network == self.network_uuid, "disconnect from",
719 [instance_info for (_, instance_info) in
720 self.cfg.GetMultiInstanceInfoByName(owned_instances)])
722 def Exec(self, feedback_fn):
723 # Disconnect the network and update the group only if network is connected
725 del self.group.networks[self.network_uuid]
726 self.cfg.Update(self.group, feedback_fn)