1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Logical units dealing with networks."""
23
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
38 """Builds network related env variables for hooks
39
40 This builds the hook environment from individual variables.
41
42 @type name: string
43 @param name: the name of the network
44 @type subnet: string
45 @param subnet: the ipv4 subnet
46 @type gateway: string
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
54 @type tags: list
55 @param tags: the tags of the network
56
57 """
58 env = {}
59 if name:
60 env["NETWORK_NAME"] = name
61 if subnet:
62 env["NETWORK_SUBNET"] = subnet
63 if gateway:
64 env["NETWORK_GATEWAY"] = gateway
65 if network6:
66 env["NETWORK_SUBNET6"] = network6
67 if gateway6:
68 env["NETWORK_GATEWAY6"] = gateway6
69 if mac_prefix:
70 env["NETWORK_MAC_PREFIX"] = mac_prefix
71 if tags:
72 env["NETWORK_TAGS"] = " ".join(tags)
73
74 return env
75
78 """Logical unit for creating networks.
79
80 """
81 HPATH = "network-add"
82 HTYPE = constants.HTYPE_NETWORK
83 REQ_BGL = False
84
86 """Build hooks nodes.
87
88 """
89 mn = self.cfg.GetMasterNode()
90 return ([mn], [mn])
91
93 if self.op.mac_prefix:
94 self.op.mac_prefix = \
95 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
96
111
130
132 """Build hooks env.
133
134 """
135 args = {
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,
143 }
144 return _BuildNetworkHookEnv(**args)
145
146 - def Exec(self, feedback_fn):
147 """Add the ip pool to the cluster.
148
149 """
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
158 try:
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))
163
164
165
166
167 if self.op.conflicts_check:
168 for node in self.cfg.GetAllNodesInfo().values():
169 for ip in [node.primary_ip, node.secondary_ip]:
170 try:
171 if pool.Contains(ip):
172 pool.Reserve(ip)
173 self.LogInfo("Reserved IP address of node '%s' (%s)",
174 node.name, ip)
175 except errors.AddressPoolError, err:
176 self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
177 ip, node.name, err)
178
179 master_ip = self.cfg.GetClusterInfo().master_ip
180 try:
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",
186 master_ip, err)
187
188 if self.op.add_reserved_ips:
189 for ip in self.op.add_reserved_ips:
190 try:
191 pool.Reserve(ip, external=True)
192 except errors.AddressPoolError, err:
193 raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
194 (ip, err))
195
196 if self.op.tags:
197 for tag in self.op.tags:
198 nobj.AddTag(tag)
199
200 self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
201 del self.remove_locks[locking.LEVEL_NETWORK]
202
205 HPATH = "network-remove"
206 HTYPE = constants.HTYPE_NETWORK
207 REQ_BGL = False
208
210 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
211
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,
216 }
217
219 """Check prerequisites.
220
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
223 cluster.
224
225 """
226
227 node_groups = [group.name
228 for group in self.cfg.GetAllNodeGroupsInfo().values()
229 if self.network_uuid in group.networks]
230
231 if node_groups:
232 self.LogWarning("Network '%s' is connected to the following"
233 " node groups: %s" %
234 (self.op.network_name,
235 utils.CommaJoin(utils.NiceSort(node_groups))))
236 raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
237
239 """Build hooks env.
240
241 """
242 return {
243 "NETWORK_NAME": self.op.network_name,
244 }
245
247 """Build hooks nodes.
248
249 """
250 mn = self.cfg.GetMasterNode()
251 return ([mn], [mn])
252
253 - def Exec(self, feedback_fn):
262
265 """Modifies the parameters of a network.
266
267 """
268 HPATH = "network-modify"
269 HTYPE = constants.HTYPE_NETWORK
270 REQ_BGL = False
271
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)
277
279 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
280
281 self.needed_locks = {
282 locking.LEVEL_NETWORK: [self.network_uuid],
283 }
284
286 """Check prerequisites.
287
288 """
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
295
296 self.pool = network.AddressPool(self.network)
297
298 if self.op.gateway:
299 if self.op.gateway == constants.VALUE_NONE:
300 self.gateway = None
301 else:
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,
306 errors.ECODE_STATE)
307
308 if self.op.mac_prefix:
309 if self.op.mac_prefix == constants.VALUE_NONE:
310 self.mac_prefix = None
311 else:
312 self.mac_prefix = \
313 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
314
315 if self.op.gateway6:
316 if self.op.gateway6 == constants.VALUE_NONE:
317 self.gateway6 = None
318 else:
319 self.gateway6 = self.op.gateway6
320
321 if self.op.network6:
322 if self.op.network6 == constants.VALUE_NONE:
323 self.network6 = None
324 else:
325 self.network6 = self.op.network6
326
328 """Build hooks env.
329
330 """
331 args = {
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,
338 "tags": self.tags,
339 }
340 return _BuildNetworkHookEnv(**args)
341
343 """Build hooks nodes.
344
345 """
346 mn = self.cfg.GetMasterNode()
347 return ([mn], [mn])
348
349 - def Exec(self, feedback_fn):
350 """Modifies the network.
351
352 """
353
354
355 if self.op.gateway:
356 if self.gateway == self.network.gateway:
357 self.LogWarning("Gateway is already %s", self.gateway)
358 else:
359 if 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
364
365 if self.op.add_reserved_ips:
366 for ip in self.op.add_reserved_ips:
367 try:
368 if self.pool.IsReserved(ip):
369 self.LogWarning("IP address %s is already reserved", ip)
370 else:
371 self.pool.Reserve(ip, external=True)
372 except errors.AddressPoolError, err:
373 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
374
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")
379 continue
380 try:
381 if not self.pool.IsReserved(ip):
382 self.LogWarning("IP address %s is already unreserved", ip)
383 else:
384 self.pool.Release(ip, external=True)
385 except errors.AddressPoolError, err:
386 self.LogWarning("Cannot release IP address %s: %s", ip, err)
387
388 if self.op.mac_prefix:
389 self.network.mac_prefix = self.mac_prefix
390
391 if self.op.network6:
392 self.network.network6 = self.network6
393
394 if self.op.gateway6:
395 self.network.gateway6 = self.gateway6
396
397 self.pool.Validate()
398
399 self.cfg.Update(self.network, feedback_fn)
400
403 FIELDS = query.NETWORK_FIELDS
404
406 lu.needed_locks = {}
407 lu.share_locks = ShareAll()
408
409 self.do_locking = self.use_locking
410
411 all_networks = lu.cfg.GetAllNetworksInfo()
412 name_to_uuid = dict((n.name, n.uuid) for n in all_networks.values())
413
414 if self.names:
415 missing = []
416 self.wanted = []
417
418 for name in self.names:
419 if name in name_to_uuid:
420 self.wanted.append(name_to_uuid[name])
421 else:
422 missing.append(name)
423
424 if missing:
425 raise errors.OpPrereqError("Some networks do not exist: %s" % missing,
426 errors.ECODE_NOENT)
427 else:
428 self.wanted = locking.ALL_SET
429
430 if self.do_locking:
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
436
439
441 """Computes the list of networks and their attributes.
442
443 """
444 all_networks = lu.cfg.GetAllNetworksInfo()
445
446 network_uuids = self._GetNames(lu, all_networks.keys(),
447 locking.LEVEL_NETWORK)
448
449 do_instances = query.NETQ_INST in self.requested_data
450 do_groups = query.NETQ_GROUP in self.requested_data
451
452 network_to_instances = None
453 network_to_groups = None
454
455
456 if do_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)
462 if netparams:
463 info = (group.name, netparams[constants.NIC_MODE],
464 netparams[constants.NIC_LINK])
465
466 network_to_groups[net_uuid].append(info)
467
468 if do_instances:
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)
475 break
476
477 if query.NETQ_STATS in self.requested_data:
478 stats = \
479 dict((uuid,
480 self._GetStats(network.AddressPool(all_networks[uuid])))
481 for uuid in network_uuids)
482 else:
483 stats = None
484
485 return query.NetworkQueryData([all_networks[uuid]
486 for uuid in network_uuids],
487 network_to_groups,
488 network_to_instances,
489 stats)
490
491 @staticmethod
503
506 """Logical unit for querying networks.
507
508 """
509 REQ_BGL = False
510
514
517
518 - def Exec(self, feedback_fn):
520
523 """Utility for L{_NetworkConflictCheck}.
524
525 """
526 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
527 for (idx, ipaddr) in details)
528
531 """Checks for network interface conflicts with a network.
532
533 @type lu: L{LogicalUnit}
534 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
535 returning boolean
536 @param check_fn: Function checking for conflict
537 @type action: string
538 @param action: Part of error message (see code)
539 @raise errors.OpPrereqError: If conflicting IP addresses are found.
540
541 """
542 conflicts = []
543
544 for (_, instance) in lu.cfg.GetMultiInstanceInfo(instances):
545 instconflicts = [(idx, nic.ip)
546 for (idx, nic) in enumerate(instance.nics)
547 if check_fn(nic)]
548
549 if instconflicts:
550 conflicts.append((instance.name, instconflicts))
551
552 if conflicts:
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)))
559
560 raise errors.OpPrereqError("Conflicting IP addresses found; "
561 " remove/modify the corresponding network"
562 " interfaces", errors.ECODE_STATE)
563
566 """Connect a network to a nodegroup
567
568 """
569 HPATH = "network-connect"
570 HTYPE = constants.HTYPE_NETWORK
571 REQ_BGL = False
572
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
578
579 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
580 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
581
582 self.needed_locks = {
583 locking.LEVEL_INSTANCE: [],
584 locking.LEVEL_NODEGROUP: [self.group_uuid],
585 }
586 self.share_locks[locking.LEVEL_INSTANCE] = 1
587
588 if self.op.conflicts_check:
589 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
590 self.share_locks[locking.LEVEL_NETWORK] = 1
591
593 if level == locking.LEVEL_INSTANCE:
594 assert not self.needed_locks[locking.LEVEL_INSTANCE]
595
596
597
598 if self.op.conflicts_check:
599 self.needed_locks[locking.LEVEL_INSTANCE] = \
600 self.cfg.GetNodeGroupInstances(self.group_uuid)
601
603 ret = {
604 "GROUP_NAME": self.group_name,
605 "GROUP_NETWORK_MODE": self.network_mode,
606 "GROUP_NETWORK_LINK": self.network_link,
607 }
608 return ret
609
611 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
612 return (nodes, nodes)
613
645
646 - def Exec(self, feedback_fn):
647
648 if not self.connected:
649 self.group.networks[self.network_uuid] = self.netparams
650 self.cfg.Update(self.group, feedback_fn)
651
654 """Disconnect a network to a nodegroup
655
656 """
657 HPATH = "network-disconnect"
658 HTYPE = constants.HTYPE_NETWORK
659 REQ_BGL = False
660
662 self.network_name = self.op.network_name
663 self.group_name = self.op.group_name
664
665 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
666 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
667
668 self.needed_locks = {
669 locking.LEVEL_INSTANCE: [],
670 locking.LEVEL_NODEGROUP: [self.group_uuid],
671 }
672 self.share_locks[locking.LEVEL_INSTANCE] = 1
673
675 if level == locking.LEVEL_INSTANCE:
676 assert not self.needed_locks[locking.LEVEL_INSTANCE]
677
678
679
680 self.needed_locks[locking.LEVEL_INSTANCE] = \
681 self.cfg.GetNodeGroupInstances(self.group_uuid)
682
684 ret = {
685 "GROUP_NAME": self.group_name,
686 }
687 return ret
688
690 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
691 return (nodes, nodes)
692
694 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
695
696 assert self.group_uuid in owned_groups
697
698
699 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
700 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
701
702 self.group = self.cfg.GetNodeGroup(self.group_uuid)
703 self.connected = True
704 if self.network_uuid not in self.group.networks:
705 self.LogWarning("Network '%s' is not mapped to group '%s'",
706 self.network_name, self.group.name)
707 self.connected = False
708
709
710 else:
711 _NetworkConflictCheck(self, lambda nic: nic.network == self.network_uuid,
712 "disconnect from", owned_instances)
713
714 - def Exec(self, feedback_fn):
715
716 if self.connected:
717 del self.group.networks[self.network_uuid]
718 self.cfg.Update(self.group, feedback_fn)
719