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.uuid)
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 @param instances: the instances to check
540 @type instances: list of instance objects
541 @raise errors.OpPrereqError: If conflicting IP addresses are found.
542
543 """
544 conflicts = []
545
546 for instance in instances:
547 instconflicts = [(idx, nic.ip)
548 for (idx, nic) in enumerate(instance.nics)
549 if check_fn(nic)]
550
551 if instconflicts:
552 conflicts.append((instance.name, instconflicts))
553
554 if conflicts:
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)))
561
562 raise errors.OpPrereqError("Conflicting IP addresses found; "
563 " remove/modify the corresponding network"
564 " interfaces", errors.ECODE_STATE)
565
568 """Connect a network to a nodegroup
569
570 """
571 HPATH = "network-connect"
572 HTYPE = constants.HTYPE_NETWORK
573 REQ_BGL = False
574
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
580
581 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
582 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
583
584 self.needed_locks = {
585 locking.LEVEL_INSTANCE: [],
586 locking.LEVEL_NODEGROUP: [self.group_uuid],
587 }
588 self.share_locks[locking.LEVEL_INSTANCE] = 1
589
590 if self.op.conflicts_check:
591 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
592 self.share_locks[locking.LEVEL_NETWORK] = 1
593
604
606 ret = {
607 "GROUP_NAME": self.group_name,
608 "GROUP_NETWORK_MODE": self.network_mode,
609 "GROUP_NETWORK_LINK": self.network_link,
610 }
611 return ret
612
614 node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
615 return (node_uuids, node_uuids)
616
650
651 - def Exec(self, feedback_fn):
652
653 if not self.connected:
654 self.group.networks[self.network_uuid] = self.netparams
655 self.cfg.Update(self.group, feedback_fn)
656
659 """Disconnect a network to a nodegroup
660
661 """
662 HPATH = "network-disconnect"
663 HTYPE = constants.HTYPE_NETWORK
664 REQ_BGL = False
665
667 self.network_name = self.op.network_name
668 self.group_name = self.op.group_name
669
670 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
671 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
672
673 self.needed_locks = {
674 locking.LEVEL_INSTANCE: [],
675 locking.LEVEL_NODEGROUP: [self.group_uuid],
676 }
677 self.share_locks[locking.LEVEL_INSTANCE] = 1
678
688
690 ret = {
691 "GROUP_NAME": self.group_name,
692 }
693 return ret
694
696 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
697 return (nodes, nodes)
698
700 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
701
702 assert self.group_uuid in owned_groups
703
704
705 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
706 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
707
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
714
715
716 else:
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)])
721
722 - def Exec(self, feedback_fn):
723
724 if self.connected:
725 del self.group.networks[self.network_uuid]
726 self.cfg.Update(self.group, feedback_fn)
727