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 utils
39 from ganeti.cmdlib.base import LogicalUnit
40 from ganeti.cmdlib.common import CheckNodeGroupInstances
41
42
45 """Builds network related env variables for hooks
46
47 This builds the hook environment from individual variables.
48
49 @type name: string
50 @param name: the name of the network
51 @type subnet: string
52 @param subnet: the ipv4 subnet
53 @type gateway: string
54 @param gateway: the ipv4 gateway
55 @type network6: string
56 @param network6: the ipv6 subnet
57 @type gateway6: string
58 @param gateway6: the ipv6 gateway
59 @type mac_prefix: string
60 @param mac_prefix: the mac_prefix
61 @type tags: list
62 @param tags: the tags of the network
63
64 """
65 env = {}
66 if name:
67 env["NETWORK_NAME"] = name
68 if subnet:
69 env["NETWORK_SUBNET"] = subnet
70 if gateway:
71 env["NETWORK_GATEWAY"] = gateway
72 if network6:
73 env["NETWORK_SUBNET6"] = network6
74 if gateway6:
75 env["NETWORK_GATEWAY6"] = gateway6
76 if mac_prefix:
77 env["NETWORK_MAC_PREFIX"] = mac_prefix
78 if tags:
79 env["NETWORK_TAGS"] = " ".join(tags)
80
81 return env
82
83
85 """Logical unit for creating networks.
86
87 """
88 HPATH = "network-add"
89 HTYPE = constants.HTYPE_NETWORK
90 REQ_BGL = False
91
93 """Build hooks nodes.
94
95 """
96 mn = self.cfg.GetMasterNode()
97 return ([mn], [mn])
98
100 if self.op.mac_prefix:
101 self.op.mac_prefix = \
102 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
103
118
137
139 """Build hooks env.
140
141 """
142 args = {
143 "name": self.op.network_name,
144 "subnet": self.op.network,
145 "gateway": self.op.gateway,
146 "network6": self.op.network6,
147 "gateway6": self.op.gateway6,
148 "mac_prefix": self.op.mac_prefix,
149 "tags": self.op.tags,
150 }
151 return _BuildNetworkHookEnv(**args)
152
153 - def Exec(self, feedback_fn):
154 """Add the ip pool to the cluster.
155
156 """
157 nobj = objects.Network(name=self.op.network_name,
158 network=self.op.network,
159 gateway=self.op.gateway,
160 network6=self.op.network6,
161 gateway6=self.op.gateway6,
162 mac_prefix=self.op.mac_prefix,
163 uuid=self.network_uuid)
164
165 try:
166 pool = network.AddressPool.InitializeNetwork(nobj)
167 except errors.AddressPoolError, err:
168 raise errors.OpExecError("Cannot create IP address pool for network"
169 " '%s': %s" % (self.op.network_name, err))
170
171
172
173
174 if self.op.conflicts_check:
175 for node in self.cfg.GetAllNodesInfo().values():
176 for ip in [node.primary_ip, node.secondary_ip]:
177 try:
178 if pool.Contains(ip):
179 pool.Reserve(ip, external=True)
180 self.LogInfo("Reserved IP address of node '%s' (%s)",
181 node.name, ip)
182 except errors.AddressPoolError, err:
183 self.LogWarning("Cannot reserve IP address '%s' of node '%s': %s",
184 ip, node.name, err)
185
186 master_ip = self.cfg.GetClusterInfo().master_ip
187 try:
188 if pool.Contains(master_ip):
189 pool.Reserve(master_ip, external=True)
190 self.LogInfo("Reserved cluster master IP address (%s)", master_ip)
191 except errors.AddressPoolError, err:
192 self.LogWarning("Cannot reserve cluster master IP address (%s): %s",
193 master_ip, err)
194
195 if self.op.add_reserved_ips:
196 for ip in self.op.add_reserved_ips:
197 try:
198 pool.Reserve(ip, external=True)
199 except errors.AddressPoolError, err:
200 raise errors.OpExecError("Cannot reserve IP address '%s': %s" %
201 (ip, err))
202
203 if self.op.tags:
204 for tag in self.op.tags:
205 nobj.AddTag(tag)
206
207 self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
208 del self.remove_locks[locking.LEVEL_NETWORK]
209
210
212 HPATH = "network-remove"
213 HTYPE = constants.HTYPE_NETWORK
214 REQ_BGL = False
215
217 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
218
219 self.share_locks[locking.LEVEL_NODEGROUP] = 1
220 self.needed_locks = {
221 locking.LEVEL_NETWORK: [self.network_uuid],
222 locking.LEVEL_NODEGROUP: locking.ALL_SET,
223 }
224
226 """Check prerequisites.
227
228 This checks that the given network name exists as a network, that is
229 empty (i.e., contains no nodes), and that is not the last group of the
230 cluster.
231
232 """
233
234 node_groups = [group.name
235 for group in self.cfg.GetAllNodeGroupsInfo().values()
236 if self.network_uuid in group.networks]
237
238 if node_groups:
239 self.LogWarning("Network '%s' is connected to the following"
240 " node groups: %s" %
241 (self.op.network_name,
242 utils.CommaJoin(utils.NiceSort(node_groups))))
243 raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
244
246 """Build hooks env.
247
248 """
249 return {
250 "NETWORK_NAME": self.op.network_name,
251 }
252
254 """Build hooks nodes.
255
256 """
257 mn = self.cfg.GetMasterNode()
258 return ([mn], [mn])
259
260 - def Exec(self, feedback_fn):
269
270
272 """Modifies the parameters of a network.
273
274 """
275 HPATH = "network-modify"
276 HTYPE = constants.HTYPE_NETWORK
277 REQ_BGL = False
278
280 if (self.op.gateway and
281 (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
282 raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
283 " at once", errors.ECODE_INVAL)
284
286 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
287
288 self.needed_locks = {
289 locking.LEVEL_NETWORK: [self.network_uuid],
290 }
291
293 """Check prerequisites.
294
295 """
296 self.network = self.cfg.GetNetwork(self.network_uuid)
297 self.gateway = self.network.gateway
298 self.mac_prefix = self.network.mac_prefix
299 self.network6 = self.network.network6
300 self.gateway6 = self.network.gateway6
301 self.tags = self.network.tags
302
303 self.pool = network.AddressPool(self.network)
304
305 if self.op.gateway:
306 if self.op.gateway == constants.VALUE_NONE:
307 self.gateway = None
308 else:
309 self.gateway = self.op.gateway
310 if self.pool.IsReserved(self.gateway):
311 raise errors.OpPrereqError("Gateway IP address '%s' is already"
312 " reserved" % self.gateway,
313 errors.ECODE_STATE)
314
315 if self.op.mac_prefix:
316 if self.op.mac_prefix == constants.VALUE_NONE:
317 self.mac_prefix = None
318 else:
319 self.mac_prefix = \
320 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
321
322 if self.op.gateway6:
323 if self.op.gateway6 == constants.VALUE_NONE:
324 self.gateway6 = None
325 else:
326 self.gateway6 = self.op.gateway6
327
328 if self.op.network6:
329 if self.op.network6 == constants.VALUE_NONE:
330 self.network6 = None
331 else:
332 self.network6 = self.op.network6
333
335 """Build hooks env.
336
337 """
338 args = {
339 "name": self.op.network_name,
340 "subnet": self.network.network,
341 "gateway": self.gateway,
342 "network6": self.network6,
343 "gateway6": self.gateway6,
344 "mac_prefix": self.mac_prefix,
345 "tags": self.tags,
346 }
347 return _BuildNetworkHookEnv(**args)
348
350 """Build hooks nodes.
351
352 """
353 mn = self.cfg.GetMasterNode()
354 return ([mn], [mn])
355
356 - def Exec(self, feedback_fn):
357 """Modifies the network.
358
359 """
360
361
362 if self.op.gateway:
363 if self.gateway == self.network.gateway:
364 self.LogWarning("Gateway is already %s", self.gateway)
365 else:
366 if self.gateway:
367 self.pool.Reserve(self.gateway, external=True)
368 if self.network.gateway:
369 self.pool.Release(self.network.gateway, external=True)
370 self.network.gateway = self.gateway
371
372 if self.op.add_reserved_ips:
373 for ip in self.op.add_reserved_ips:
374 try:
375 self.pool.Reserve(ip, external=True)
376 except errors.AddressPoolError, err:
377 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
378
379 if self.op.remove_reserved_ips:
380 for ip in self.op.remove_reserved_ips:
381 if ip == self.network.gateway:
382 self.LogWarning("Cannot unreserve Gateway's IP")
383 continue
384 try:
385 self.pool.Release(ip, external=True)
386 except errors.AddressPoolError, err:
387 self.LogWarning("Cannot release IP address %s: %s", ip, err)
388
389 if self.op.mac_prefix:
390 self.network.mac_prefix = self.mac_prefix
391
392 if self.op.network6:
393 self.network.network6 = self.network6
394
395 if self.op.gateway6:
396 self.network.gateway6 = self.gateway6
397
398 self.pool.Validate()
399
400 self.cfg.Update(self.network, feedback_fn)
401
402
404 """Utility for L{_NetworkConflictCheck}.
405
406 """
407 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
408 for (idx, ipaddr) in details)
409
410
412 """Checks for network interface conflicts with a network.
413
414 @type lu: L{LogicalUnit}
415 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
416 returning boolean
417 @param check_fn: Function checking for conflict
418 @type action: string
419 @param action: Part of error message (see code)
420 @param instances: the instances to check
421 @type instances: list of instance objects
422 @raise errors.OpPrereqError: If conflicting IP addresses are found.
423
424 """
425 conflicts = []
426
427 for instance in instances:
428 instconflicts = [(idx, nic.ip)
429 for (idx, nic) in enumerate(instance.nics)
430 if check_fn(nic)]
431
432 if instconflicts:
433 conflicts.append((instance.name, instconflicts))
434
435 if conflicts:
436 lu.LogWarning("IP addresses from network '%s', which is about to %s"
437 " node group '%s', are in use: %s" %
438 (lu.network_name, action, lu.group.name,
439 utils.CommaJoin(("%s: %s" %
440 (name, _FmtNetworkConflict(details)))
441 for (name, details) in conflicts)))
442
443 raise errors.OpPrereqError("Conflicting IP addresses found; "
444 " remove/modify the corresponding network"
445 " interfaces", errors.ECODE_STATE)
446
447
449 """Connect a network to a nodegroup
450
451 """
452 HPATH = "network-connect"
453 HTYPE = constants.HTYPE_NETWORK
454 REQ_BGL = False
455
457 self.network_name = self.op.network_name
458 self.group_name = self.op.group_name
459 self.network_mode = self.op.network_mode
460 self.network_link = self.op.network_link
461 self.network_vlan = self.op.network_vlan
462
463 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
464 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
465
466 self.needed_locks = {
467 locking.LEVEL_INSTANCE: [],
468 locking.LEVEL_NODEGROUP: [self.group_uuid],
469 }
470 self.share_locks[locking.LEVEL_INSTANCE] = 1
471
472 if self.op.conflicts_check:
473 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
474 self.share_locks[locking.LEVEL_NETWORK] = 1
475
486
488 ret = {
489 "GROUP_NAME": self.group_name,
490 "GROUP_NETWORK_MODE": self.network_mode,
491 "GROUP_NETWORK_LINK": self.network_link,
492 "GROUP_NETWORK_VLAN": self.network_vlan,
493 }
494 return ret
495
497 node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
498 return (node_uuids, node_uuids)
499
501 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
502
503 assert self.group_uuid in owned_groups
504
505
506 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
507 if self.op.conflicts_check:
508 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
509
510 self.netparams = {
511 constants.NIC_MODE: self.network_mode,
512 constants.NIC_LINK: self.network_link,
513 constants.NIC_VLAN: self.network_vlan,
514 }
515
516 objects.NIC.CheckParameterSyntax(self.netparams)
517
518 self.group = self.cfg.GetNodeGroup(self.group_uuid)
519
520
521 self.connected = False
522 if self.network_uuid in self.group.networks:
523 self.LogWarning("Network '%s' is already mapped to group '%s'" %
524 (self.network_name, self.group.name))
525 self.connected = True
526
527
528 elif self.op.conflicts_check:
529 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
530
531 _NetworkConflictCheck(
532 self, lambda nic: pool.Contains(nic.ip), "connect to",
533 [instance_info for (_, instance_info) in
534 self.cfg.GetMultiInstanceInfoByName(owned_instance_names)])
535
536 - def Exec(self, feedback_fn):
537
538 if not self.connected:
539 self.group.networks[self.network_uuid] = self.netparams
540 self.cfg.Update(self.group, feedback_fn)
541
542
544 """Disconnect a network to a nodegroup
545
546 """
547 HPATH = "network-disconnect"
548 HTYPE = constants.HTYPE_NETWORK
549 REQ_BGL = False
550
552 self.network_name = self.op.network_name
553 self.group_name = self.op.group_name
554
555 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
556 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
557
558 self.needed_locks = {
559 locking.LEVEL_INSTANCE: [],
560 locking.LEVEL_NODEGROUP: [self.group_uuid],
561 }
562 self.share_locks[locking.LEVEL_INSTANCE] = 1
563
573
575 ret = {
576 "GROUP_NAME": self.group_name,
577 }
578
579 if self.connected:
580 ret.update({
581 "GROUP_NETWORK_MODE": self.netparams[constants.NIC_MODE],
582 "GROUP_NETWORK_LINK": self.netparams[constants.NIC_LINK],
583 "GROUP_NETWORK_VLAN": self.netparams[constants.NIC_VLAN],
584 })
585 return ret
586
588 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
589 return (nodes, nodes)
590
592 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
593
594 assert self.group_uuid in owned_groups
595
596
597 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
598 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
599
600 self.group = self.cfg.GetNodeGroup(self.group_uuid)
601 self.connected = True
602 if self.network_uuid not in self.group.networks:
603 self.LogWarning("Network '%s' is not mapped to group '%s'",
604 self.network_name, self.group.name)
605 self.connected = False
606
607
608 else:
609 _NetworkConflictCheck(
610 self, lambda nic: nic.network == self.network_uuid, "disconnect from",
611 [instance_info for (_, instance_info) in
612 self.cfg.GetMultiInstanceInfoByName(owned_instances)])
613 self.netparams = self.group.networks.get(self.network_uuid)
614
615 - def Exec(self, feedback_fn):
616
617 if self.connected:
618 del self.group.networks[self.network_uuid]
619 self.cfg.Update(self.group, feedback_fn)
620