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 self.pool.Reserve(ip, external=True)
369 except errors.AddressPoolError, err:
370 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
372 if self.op.remove_reserved_ips:
373 for ip in self.op.remove_reserved_ips:
374 if ip == self.network.gateway:
375 self.LogWarning("Cannot unreserve Gateway's IP")
378 self.pool.Release(ip, external=True)
379 except errors.AddressPoolError, err:
380 self.LogWarning("Cannot release IP address %s: %s", ip, err)
382 if self.op.mac_prefix:
383 self.network.mac_prefix = self.mac_prefix
386 self.network.network6 = self.network6
389 self.network.gateway6 = self.gateway6
393 self.cfg.Update(self.network, feedback_fn)
396 class NetworkQuery(QueryBase):
397 FIELDS = query.NETWORK_FIELDS
399 def ExpandNames(self, lu):
401 lu.share_locks = ShareAll()
403 self.do_locking = self.use_locking
405 all_networks = lu.cfg.GetAllNetworksInfo()
406 name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
412 for name in self.names:
413 if name in name_to_uuid:
414 self.wanted.append(name_to_uuid[name])
419 raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
422 self.wanted = locking.ALL_SET
425 lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted
426 if query.NETQ_INST in self.requested_data:
427 lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
428 if query.NETQ_GROUP in self.requested_data:
429 lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
431 def DeclareLocks(self, lu, level):
434 def _GetQueryData(self, lu):
435 """Computes the list of networks and their attributes.
438 all_networks = lu.cfg.GetAllNetworksInfo()
440 network_uuids = self._GetNames(lu, all_networks.keys(),
441 locking.LEVEL_NETWORK)
443 do_instances = query.NETQ_INST in self.requested_data
444 do_groups = query.NETQ_GROUP in self.requested_data
446 network_to_instances = None
447 network_to_groups = None
449 # For NETQ_GROUP, we need to map network->[groups]
451 all_groups = lu.cfg.GetAllNodeGroupsInfo()
452 network_to_groups = dict((uuid, []) for uuid in network_uuids)
453 for _, group in all_groups.iteritems():
454 for net_uuid in network_uuids:
455 netparams = group.networks.get(net_uuid, None)
457 info = (group.name, netparams[constants.NIC_MODE],
458 netparams[constants.NIC_LINK])
460 network_to_groups[net_uuid].append(info)
463 all_instances = lu.cfg.GetAllInstancesInfo()
464 network_to_instances = dict((uuid, []) for uuid in network_uuids)
465 for instance in all_instances.values():
466 for nic in instance.nics:
467 if nic.network in network_uuids:
468 network_to_instances[nic.network].append(instance.name)
471 if query.NETQ_STATS in self.requested_data:
474 self._GetStats(network.AddressPool(all_networks[uuid])))
475 for uuid in network_uuids)
479 return query.NetworkQueryData([all_networks[uuid]
480 for uuid in network_uuids],
482 network_to_instances,
487 """Returns statistics for a network address pool.
491 "free_count": pool.GetFreeCount(),
492 "reserved_count": pool.GetReservedCount(),
493 "map": pool.GetMap(),
494 "external_reservations":
495 utils.CommaJoin(pool.GetExternalReservations()),
499 class LUNetworkQuery(NoHooksLU):
500 """Logical unit for querying networks.
505 def CheckArguments(self):
506 self.nq = NetworkQuery(qlang.MakeSimpleFilter("name", self.op.names),
507 self.op.output_fields, self.op.use_locking)
509 def ExpandNames(self):
510 self.nq.ExpandNames(self)
512 def Exec(self, feedback_fn):
513 return self.nq.OldStyleQuery(self)
516 def _FmtNetworkConflict(details):
517 """Utility for L{_NetworkConflictCheck}.
520 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
521 for (idx, ipaddr) in details)
524 def _NetworkConflictCheck(lu, check_fn, action, instances):
525 """Checks for network interface conflicts with a network.
527 @type lu: L{LogicalUnit}
528 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
530 @param check_fn: Function checking for conflict
532 @param action: Part of error message (see code)
533 @raise errors.OpPrereqError: If conflicting IP addresses are found.
538 for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
539 instconflicts = [(idx, nic.ip)
540 for (idx, nic) in enumerate(instance.nics)
544 conflicts.append((instance.name, instconflicts))
547 lu.LogWarning("IP addresses from network '%s', which is about to %s"
548 " node group '%s', are in use: %s" %
549 (lu.network_name, action, lu.group.name,
550 utils.CommaJoin(("%s: %s" %
551 (name, _FmtNetworkConflict(details)))
552 for (name, details) in conflicts)))
554 raise errors.OpPrereqError("Conflicting IP addresses found; "
555 " remove/modify the corresponding network"
556 " interfaces", errors.ECODE_STATE)
559 class LUNetworkConnect(LogicalUnit):
560 """Connect a network to a nodegroup
563 HPATH = "network-connect"
564 HTYPE = constants.HTYPE_NETWORK
567 def ExpandNames(self):
568 self.network_name = self.op.network_name
569 self.group_name = self.op.group_name
570 self.network_mode = self.op.network_mode
571 self.network_link = self.op.network_link
573 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
574 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
576 self.needed_locks = {
577 locking.LEVEL_NODEGROUP: [self.group_uuid],
580 if self.op.conflicts_check:
581 self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
582 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
583 self.share_locks[locking.LEVEL_NETWORK] = 1
584 self.share_locks[locking.LEVEL_INSTANCE] = 1
586 def DeclareLocks(self, level):
589 def BuildHooksEnv(self):
591 "GROUP_NAME": self.group_name,
592 "GROUP_NETWORK_MODE": self.network_mode,
593 "GROUP_NETWORK_LINK": self.network_link,
597 def BuildHooksNodes(self):
598 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
599 return (nodes, nodes)
601 def CheckPrereq(self):
602 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
604 assert self.group_uuid in owned_groups
606 # Check if locked instances are still correct
607 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
610 constants.NIC_MODE: self.network_mode,
611 constants.NIC_LINK: self.network_link,
613 objects.NIC.CheckParameterSyntax(self.netparams)
615 self.group = self.cfg.GetNodeGroup(self.group_uuid)
616 #if self.network_mode == constants.NIC_MODE_BRIDGED:
617 # _CheckNodeGroupBridgesExist(self, self.network_link, self.group_uuid)
618 self.connected = False
619 if self.network_uuid in self.group.networks:
620 self.LogWarning("Network '%s' is already mapped to group '%s'" %
621 (self.network_name, self.group.name))
622 self.connected = True
624 # check only if not already connected
625 elif self.op.conflicts_check:
626 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
628 _NetworkConflictCheck(self, lambda nic: pool.Contains(nic.ip),
629 "connect to", owned_instances)
631 def Exec(self, feedback_fn):
632 # Connect the network and update the group only if not already connected
633 if not self.connected:
634 self.group.networks[self.network_uuid] = self.netparams
635 self.cfg.Update(self.group, feedback_fn)
638 class LUNetworkDisconnect(LogicalUnit):
639 """Disconnect a network to a nodegroup
642 HPATH = "network-disconnect"
643 HTYPE = constants.HTYPE_NETWORK
646 def ExpandNames(self):
647 self.network_name = self.op.network_name
648 self.group_name = self.op.group_name
650 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
651 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
653 self.needed_locks = {
654 locking.LEVEL_INSTANCE: locking.ALL_SET,
655 locking.LEVEL_NODEGROUP: [self.group_uuid],
657 self.share_locks[locking.LEVEL_INSTANCE] = 1
659 def DeclareLocks(self, level):
662 def BuildHooksEnv(self):
664 "GROUP_NAME": self.group_name,
668 def BuildHooksNodes(self):
669 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
670 return (nodes, nodes)
672 def CheckPrereq(self):
673 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
675 assert self.group_uuid in owned_groups
677 # Check if locked instances are still correct
678 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
680 self.group = self.cfg.GetNodeGroup(self.group_uuid)
681 self.connected = True
682 if self.network_uuid not in self.group.networks:
683 self.LogWarning("Network '%s' is not mapped to group '%s'",
684 self.network_name, self.group.name)
685 self.connected = False
687 # We need this check only if network is not already connected
689 _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
690 "disconnect from", owned_instances)
692 def Exec(self, feedback_fn):
693 # Disconnect the network and update the group only if network is connected
695 del self.group.networks[self.network_uuid]
696 self.cfg.Update(self.group, feedback_fn)