1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 """Logical units dealing with networks."""
32
33 from ganeti import constants
34 from ganeti import errors
35 from ganeti import locking
36 from ganeti import network
37 from ganeti import objects
38 from ganeti import qlang
39 from ganeti import query
40 from ganeti import utils
41 from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase
42 from ganeti.cmdlib.common import ShareAll, CheckNodeGroupInstances
47 """Builds network related env variables for hooks
48
49 This builds the hook environment from individual variables.
50
51 @type name: string
52 @param name: the name of the network
53 @type subnet: string
54 @param subnet: the ipv4 subnet
55 @type gateway: string
56 @param gateway: the ipv4 gateway
57 @type network6: string
58 @param network6: the ipv6 subnet
59 @type gateway6: string
60 @param gateway6: the ipv6 gateway
61 @type mac_prefix: string
62 @param mac_prefix: the mac_prefix
63 @type tags: list
64 @param tags: the tags of the network
65
66 """
67 env = {}
68 if name:
69 env["NETWORK_NAME"] = name
70 if subnet:
71 env["NETWORK_SUBNET"] = subnet
72 if gateway:
73 env["NETWORK_GATEWAY"] = gateway
74 if network6:
75 env["NETWORK_SUBNET6"] = network6
76 if gateway6:
77 env["NETWORK_GATEWAY6"] = gateway6
78 if mac_prefix:
79 env["NETWORK_MAC_PREFIX"] = mac_prefix
80 if tags:
81 env["NETWORK_TAGS"] = " ".join(tags)
82
83 return env
84
87 """Logical unit for creating networks.
88
89 """
90 HPATH = "network-add"
91 HTYPE = constants.HTYPE_NETWORK
92 REQ_BGL = False
93
95 """Build hooks nodes.
96
97 """
98 mn = self.cfg.GetMasterNode()
99 return ([mn], [mn])
100
102 if self.op.mac_prefix:
103 self.op.mac_prefix = \
104 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
105
120
139
141 """Build hooks env.
142
143 """
144 args = {
145 "name": self.op.network_name,
146 "subnet": self.op.network,
147 "gateway": self.op.gateway,
148 "network6": self.op.network6,
149 "gateway6": self.op.gateway6,
150 "mac_prefix": self.op.mac_prefix,
151 "tags": self.op.tags,
152 }
153 return _BuildNetworkHookEnv(**args)
154
155 - def Exec(self, feedback_fn):
156 """Add the ip pool to the cluster.
157
158 """
159 nobj = objects.Network(name=self.op.network_name,
160 network=self.op.network,
161 gateway=self.op.gateway,
162 network6=self.op.network6,
163 gateway6=self.op.gateway6,
164 mac_prefix=self.op.mac_prefix,
165 uuid=self.network_uuid)
166
167 try:
168 pool = network.AddressPool.InitializeNetwork(nobj)
169 except errors.AddressPoolError, err:
170 raise errors.OpExecError("Cannot create IP address pool for network"
171 " '%s': %s" % (self.op.network_name, err))
172
173
174
175
176 if self.op.conflicts_check:
177 for node in self.cfg.GetAllNodesInfo().values():
178 for ip in [node.primary_ip, node.secondary_ip]:
179 try:
180 if pool.Contains(ip):
181 pool.Reserve(ip, external=True)
182 self.LogInfo("Reserved IP address of node '%s' (%s)",
183 node.name, ip)
184 except errors.AddressPoolError, err:
185 self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
186 ip, node.name, err)
187
188 master_ip = self.cfg.GetClusterInfo().master_ip
189 try:
190 if pool.Contains(master_ip):
191 pool.Reserve(master_ip, external=True)
192 self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
193 except errors.AddressPoolError, err:
194 self.LogWarning("Cannot reserve cluster master IP address (%s): %s",
195 master_ip, err)
196
197 if self.op.add_reserved_ips:
198 for ip in self.op.add_reserved_ips:
199 try:
200 pool.Reserve(ip, external=True)
201 except errors.AddressPoolError, err:
202 raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
203 (ip, err))
204
205 if self.op.tags:
206 for tag in self.op.tags:
207 nobj.AddTag(tag)
208
209 self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
210 del self.remove_locks[locking.LEVEL_NETWORK]
211
214 HPATH = "network-remove"
215 HTYPE = constants.HTYPE_NETWORK
216 REQ_BGL = False
217
219 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
220
221 self.share_locks[locking.LEVEL_NODEGROUP] = 1
222 self.needed_locks = {
223 locking.LEVEL_NETWORK: [self.network_uuid],
224 locking.LEVEL_NODEGROUP: locking.ALL_SET,
225 }
226
228 """Check prerequisites.
229
230 This checks that the given network name exists as a network, that is
231 empty (i.e., contains no nodes), and that is not the last group of the
232 cluster.
233
234 """
235
236 node_groups = [group.name
237 for group in self.cfg.GetAllNodeGroupsInfo().values()
238 if self.network_uuid in group.networks]
239
240 if node_groups:
241 self.LogWarning("Network '%s' is connected to the following"
242 " node groups: %s" %
243 (self.op.network_name,
244 utils.CommaJoin(utils.NiceSort(node_groups))))
245 raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
246
248 """Build hooks env.
249
250 """
251 return {
252 "NETWORK_NAME": self.op.network_name,
253 }
254
256 """Build hooks nodes.
257
258 """
259 mn = self.cfg.GetMasterNode()
260 return ([mn], [mn])
261
262 - def Exec(self, feedback_fn):
271
274 """Modifies the parameters of a network.
275
276 """
277 HPATH = "network-modify"
278 HTYPE = constants.HTYPE_NETWORK
279 REQ_BGL = False
280
282 if (self.op.gateway and
283 (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
284 raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
285 " at once", errors.ECODE_INVAL)
286
288 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
289
290 self.needed_locks = {
291 locking.LEVEL_NETWORK: [self.network_uuid],
292 }
293
295 """Check prerequisites.
296
297 """
298 self.network = self.cfg.GetNetwork(self.network_uuid)
299 self.gateway = self.network.gateway
300 self.mac_prefix = self.network.mac_prefix
301 self.network6 = self.network.network6
302 self.gateway6 = self.network.gateway6
303 self.tags = self.network.tags
304
305 self.pool = network.AddressPool(self.network)
306
307 if self.op.gateway:
308 if self.op.gateway == constants.VALUE_NONE:
309 self.gateway = None
310 else:
311 self.gateway = self.op.gateway
312 if self.pool.IsReserved(self.gateway):
313 raise errors.OpPrereqError("Gateway IP address '%s' is already"
314 " reserved" % self.gateway,
315 errors.ECODE_STATE)
316
317 if self.op.mac_prefix:
318 if self.op.mac_prefix == constants.VALUE_NONE:
319 self.mac_prefix = None
320 else:
321 self.mac_prefix = \
322 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
323
324 if self.op.gateway6:
325 if self.op.gateway6 == constants.VALUE_NONE:
326 self.gateway6 = None
327 else:
328 self.gateway6 = self.op.gateway6
329
330 if self.op.network6:
331 if self.op.network6 == constants.VALUE_NONE:
332 self.network6 = None
333 else:
334 self.network6 = self.op.network6
335
337 """Build hooks env.
338
339 """
340 args = {
341 "name": self.op.network_name,
342 "subnet": self.network.network,
343 "gateway": self.gateway,
344 "network6": self.network6,
345 "gateway6": self.gateway6,
346 "mac_prefix": self.mac_prefix,
347 "tags": self.tags,
348 }
349 return _BuildNetworkHookEnv(**args)
350
352 """Build hooks nodes.
353
354 """
355 mn = self.cfg.GetMasterNode()
356 return ([mn], [mn])
357
358 - def Exec(self, feedback_fn):
359 """Modifies the network.
360
361 """
362
363
364 if self.op.gateway:
365 if self.gateway == self.network.gateway:
366 self.LogWarning("Gateway is already %s", self.gateway)
367 else:
368 if self.gateway:
369 self.pool.Reserve(self.gateway, external=True)
370 if self.network.gateway:
371 self.pool.Release(self.network.gateway, external=True)
372 self.network.gateway = self.gateway
373
374 if self.op.add_reserved_ips:
375 for ip in self.op.add_reserved_ips:
376 try:
377 self.pool.Reserve(ip, external=True)
378 except errors.AddressPoolError, err:
379 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
380
381 if self.op.remove_reserved_ips:
382 for ip in self.op.remove_reserved_ips:
383 if ip == self.network.gateway:
384 self.LogWarning("Cannot unreserve Gateway's IP")
385 continue
386 try:
387 self.pool.Release(ip, external=True)
388 except errors.AddressPoolError, err:
389 self.LogWarning("Cannot release IP address %s: %s", ip, err)
390
391 if self.op.mac_prefix:
392 self.network.mac_prefix = self.mac_prefix
393
394 if self.op.network6:
395 self.network.network6 = self.network6
396
397 if self.op.gateway6:
398 self.network.gateway6 = self.gateway6
399
400 self.pool.Validate()
401
402 self.cfg.Update(self.network, feedback_fn)
403
406 FIELDS = query.NETWORK_FIELDS
407
409 lu.needed_locks = {}
410 lu.share_locks = ShareAll()
411
412 self.do_locking = self.use_locking
413
414 all_networks = lu.cfg.GetAllNetworksInfo()
415 name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
416
417 if self.names:
418 missing = []
419 self.wanted = []
420
421 for name in self.names:
422 if name in name_to_uuid:
423 self.wanted.append(name_to_uuid[name])
424 else:
425 missing.append(name)
426
427 if missing:
428 raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
429 errors.ECODE_NOENT)
430 else:
431 self.wanted = locking.ALL_SET
432
433 if self.do_locking:
434 lu.needed_locks[locking.LEVEL_NETWORK] = self.wanted
435 if query.NETQ_INST in self.requested_data:
436 lu.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
437 if query.NETQ_GROUP in self.requested_data:
438 lu.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
439
442
444 """Computes the list of networks and their attributes.
445
446 """
447 all_networks = lu.cfg.GetAllNetworksInfo()
448
449 network_uuids = self._GetNames(lu, all_networks.keys(),
450 locking.LEVEL_NETWORK)
451
452 do_instances = query.NETQ_INST in self.requested_data
453 do_groups = query.NETQ_GROUP in self.requested_data
454
455 network_to_instances = None
456 network_to_groups = None
457
458
459 if do_groups:
460 all_groups = lu.cfg.GetAllNodeGroupsInfo()
461 network_to_groups = dict((uuid, []) for uuid in network_uuids)
462 for _, group in all_groups.iteritems():
463 for net_uuid in network_uuids:
464 netparams = group.networks.get(net_uuid, None)
465 if netparams:
466 info = (group.name, netparams[constants.NIC_MODE],
467 netparams[constants.NIC_LINK],
468 netparams[constants.NIC_VLAN])
469
470 network_to_groups[net_uuid].append(info)
471
472 if do_instances:
473 all_instances = lu.cfg.GetAllInstancesInfo()
474 network_to_instances = dict((uuid, []) for uuid in network_uuids)
475 for instance in all_instances.values():
476 for nic in instance.nics:
477 if nic.network in network_uuids:
478 if instance.name not in network_to_instances[nic.network]:
479 network_to_instances[nic.network].append(instance.name)
480
481 if query.NETQ_STATS in self.requested_data:
482 stats = \
483 dict((uuid,
484 self._GetStats(network.AddressPool(all_networks[uuid])))
485 for uuid in network_uuids)
486 else:
487 stats = None
488
489 return query.NetworkQueryData([all_networks[uuid]
490 for uuid in network_uuids],
491 network_to_groups,
492 network_to_instances,
493 stats)
494
495 @staticmethod
507
510 """Logical unit for querying networks.
511
512 """
513 REQ_BGL = False
514
518
521
522 - def Exec(self, feedback_fn):
524
527 """Utility for L{_NetworkConflictCheck}.
528
529 """
530 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
531 for (idx, ipaddr) in details)
532
535 """Checks for network interface conflicts with a network.
536
537 @type lu: L{LogicalUnit}
538 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
539 returning boolean
540 @param check_fn: Function checking for conflict
541 @type action: string
542 @param action: Part of error message (see code)
543 @param instances: the instances to check
544 @type instances: list of instance objects
545 @raise errors.OpPrereqError: If conflicting IP addresses are found.
546
547 """
548 conflicts = []
549
550 for instance in instances:
551 instconflicts = [(idx, nic.ip)
552 for (idx, nic) in enumerate(instance.nics)
553 if check_fn(nic)]
554
555 if instconflicts:
556 conflicts.append((instance.name, instconflicts))
557
558 if conflicts:
559 lu.LogWarning("IP addresses from network '%s', which is about to %s"
560 " node group '%s', are in use: %s" %
561 (lu.network_name, action, lu.group.name,
562 utils.CommaJoin(("%s: %s" %
563 (name, _FmtNetworkConflict(details)))
564 for (name, details) in conflicts)))
565
566 raise errors.OpPrereqError("Conflicting IP addresses found; "
567 " remove/modify the corresponding network"
568 " interfaces", errors.ECODE_STATE)
569
572 """Connect a network to a nodegroup
573
574 """
575 HPATH = "network-connect"
576 HTYPE = constants.HTYPE_NETWORK
577 REQ_BGL = False
578
580 self.network_name = self.op.network_name
581 self.group_name = self.op.group_name
582 self.network_mode = self.op.network_mode
583 self.network_link = self.op.network_link
584 self.network_vlan = self.op.network_vlan
585
586 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
587 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
588
589 self.needed_locks = {
590 locking.LEVEL_INSTANCE: [],
591 locking.LEVEL_NODEGROUP: [self.group_uuid],
592 }
593 self.share_locks[locking.LEVEL_INSTANCE] = 1
594
595 if self.op.conflicts_check:
596 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
597 self.share_locks[locking.LEVEL_NETWORK] = 1
598
609
611 ret = {
612 "GROUP_NAME": self.group_name,
613 "GROUP_NETWORK_MODE": self.network_mode,
614 "GROUP_NETWORK_LINK": self.network_link,
615 "GROUP_NETWORK_VLAN": self.network_vlan,
616 }
617 return ret
618
620 node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
621 return (node_uuids, node_uuids)
622
624 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
625
626 assert self.group_uuid in owned_groups
627
628
629 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
630 if self.op.conflicts_check:
631 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
632
633 self.netparams = {
634 constants.NIC_MODE: self.network_mode,
635 constants.NIC_LINK: self.network_link,
636 constants.NIC_VLAN: self.network_vlan,
637 }
638
639 objects.NIC.CheckParameterSyntax(self.netparams)
640
641 self.group = self.cfg.GetNodeGroup(self.group_uuid)
642
643
644 self.connected = False
645 if self.network_uuid in self.group.networks:
646 self.LogWarning("Network '%s' is already mapped to group '%s'" %
647 (self.network_name, self.group.name))
648 self.connected = True
649
650
651 elif self.op.conflicts_check:
652 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
653
654 _NetworkConflictCheck(
655 self, lambda nic: pool.Contains(nic.ip), "connect to",
656 [instance_info for (_, instance_info) in
657 self.cfg.GetMultiInstanceInfoByName(owned_instance_names)])
658
659 - def Exec(self, feedback_fn):
660
661 if not self.connected:
662 self.group.networks[self.network_uuid] = self.netparams
663 self.cfg.Update(self.group, feedback_fn)
664
667 """Disconnect a network to a nodegroup
668
669 """
670 HPATH = "network-disconnect"
671 HTYPE = constants.HTYPE_NETWORK
672 REQ_BGL = False
673
675 self.network_name = self.op.network_name
676 self.group_name = self.op.group_name
677
678 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
679 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
680
681 self.needed_locks = {
682 locking.LEVEL_INSTANCE: [],
683 locking.LEVEL_NODEGROUP: [self.group_uuid],
684 }
685 self.share_locks[locking.LEVEL_INSTANCE] = 1
686
696
698 ret = {
699 "GROUP_NAME": self.group_name,
700 }
701
702 if self.connected:
703 ret.update({
704 "GROUP_NETWORK_MODE": self.netparams[constants.NIC_MODE],
705 "GROUP_NETWORK_LINK": self.netparams[constants.NIC_LINK],
706 "GROUP_NETWORK_VLAN": self.netparams[constants.NIC_VLAN],
707 })
708 return ret
709
711 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
712 return (nodes, nodes)
713
715 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
716
717 assert self.group_uuid in owned_groups
718
719
720 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
721 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
722
723 self.group = self.cfg.GetNodeGroup(self.group_uuid)
724 self.connected = True
725 if self.network_uuid not in self.group.networks:
726 self.LogWarning("Network '%s' is not mapped to group '%s'",
727 self.network_name, self.group.name)
728 self.connected = False
729
730
731 else:
732 _NetworkConflictCheck(
733 self, lambda nic: nic.network == self.network_uuid, "disconnect from",
734 [instance_info for (_, instance_info) in
735 self.cfg.GetMultiInstanceInfoByName(owned_instances)])
736 self.netparams = self.group.networks.get(self.network_uuid)
737
738 - def Exec(self, feedback_fn):
739
740 if self.connected:
741 del self.group.networks[self.network_uuid]
742 self.cfg.Update(self.group, feedback_fn)
743