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
209
211 HPATH = "network-remove"
212 HTYPE = constants.HTYPE_NETWORK
213 REQ_BGL = False
214
216 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
217
218 self.share_locks[locking.LEVEL_NODEGROUP] = 1
219 self.needed_locks = {
220 locking.LEVEL_NETWORK: [self.network_uuid],
221 locking.LEVEL_NODEGROUP: locking.ALL_SET,
222 }
223
225 """Check prerequisites.
226
227 This checks that the given network name exists as a network, that is
228 empty (i.e., contains no nodes), and that is not the last group of the
229 cluster.
230
231 """
232
233 node_groups = [group.name
234 for group in self.cfg.GetAllNodeGroupsInfo().values()
235 if self.network_uuid in group.networks]
236
237 if node_groups:
238 self.LogWarning("Network '%s' is connected to the following"
239 " node groups: %s" %
240 (self.op.network_name,
241 utils.CommaJoin(utils.NiceSort(node_groups))))
242 raise errors.OpPrereqError("Network still connected", errors.ECODE_STATE)
243
245 """Build hooks env.
246
247 """
248 return {
249 "NETWORK_NAME": self.op.network_name,
250 }
251
253 """Build hooks nodes.
254
255 """
256 mn = self.cfg.GetMasterNode()
257 return ([mn], [mn])
258
259 - def Exec(self, feedback_fn):
268
269
271 """Modifies the parameters of a network.
272
273 """
274 HPATH = "network-modify"
275 HTYPE = constants.HTYPE_NETWORK
276 REQ_BGL = False
277
279 if (self.op.gateway and
280 (self.op.add_reserved_ips or self.op.remove_reserved_ips)):
281 raise errors.OpPrereqError("Cannot modify gateway and reserved ips"
282 " at once", errors.ECODE_INVAL)
283
285 self.network_uuid = self.cfg.LookupNetwork(self.op.network_name)
286
287 self.needed_locks = {
288 locking.LEVEL_NETWORK: [self.network_uuid],
289 }
290
292 """Check prerequisites.
293
294 """
295 self.network = self.cfg.GetNetwork(self.network_uuid)
296 self.gateway = self.network.gateway
297 self.mac_prefix = self.network.mac_prefix
298 self.network6 = self.network.network6
299 self.gateway6 = self.network.gateway6
300 self.tags = self.network.tags
301
302 self.pool = network.AddressPool(self.network)
303
304 if self.op.gateway:
305 if self.op.gateway == constants.VALUE_NONE:
306 self.gateway = None
307 else:
308 self.gateway = self.op.gateway
309 if self.pool.IsReserved(self.gateway):
310 raise errors.OpPrereqError("Gateway IP address '%s' is already"
311 " reserved" % self.gateway,
312 errors.ECODE_STATE)
313
314 if self.op.mac_prefix:
315 if self.op.mac_prefix == constants.VALUE_NONE:
316 self.mac_prefix = None
317 else:
318 self.mac_prefix = \
319 utils.NormalizeAndValidateThreeOctetMacPrefix(self.op.mac_prefix)
320
321 if self.op.gateway6:
322 if self.op.gateway6 == constants.VALUE_NONE:
323 self.gateway6 = None
324 else:
325 self.gateway6 = self.op.gateway6
326
327 if self.op.network6:
328 if self.op.network6 == constants.VALUE_NONE:
329 self.network6 = None
330 else:
331 self.network6 = self.op.network6
332
334 """Build hooks env.
335
336 """
337 args = {
338 "name": self.op.network_name,
339 "subnet": self.network.network,
340 "gateway": self.gateway,
341 "network6": self.network6,
342 "gateway6": self.gateway6,
343 "mac_prefix": self.mac_prefix,
344 "tags": self.tags,
345 }
346 return _BuildNetworkHookEnv(**args)
347
349 """Build hooks nodes.
350
351 """
352 mn = self.cfg.GetMasterNode()
353 return ([mn], [mn])
354
355 - def Exec(self, feedback_fn):
356 """Modifies the network.
357
358 """
359
360
361 if self.op.gateway:
362 if self.gateway == self.network.gateway:
363 self.LogWarning("Gateway is already %s", self.gateway)
364 else:
365 if self.gateway:
366 self.pool.Reserve(self.gateway, external=True)
367 if self.network.gateway:
368 self.pool.Release(self.network.gateway, external=True)
369 self.network.gateway = self.gateway
370
371 if self.op.add_reserved_ips:
372 for ip in self.op.add_reserved_ips:
373 try:
374 self.pool.Reserve(ip, external=True)
375 except errors.AddressPoolError, err:
376 self.LogWarning("Cannot reserve IP address %s: %s", ip, err)
377
378 if self.op.remove_reserved_ips:
379 for ip in self.op.remove_reserved_ips:
380 if ip == self.network.gateway:
381 self.LogWarning("Cannot unreserve Gateway's IP")
382 continue
383 try:
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
401
403 """Utility for L{_NetworkConflictCheck}.
404
405 """
406 return utils.CommaJoin("nic%s/%s" % (idx, ipaddr)
407 for (idx, ipaddr) in details)
408
409
411 """Checks for network interface conflicts with a network.
412
413 @type lu: L{LogicalUnit}
414 @type check_fn: callable receiving one parameter (L{objects.NIC}) and
415 returning boolean
416 @param check_fn: Function checking for conflict
417 @type action: string
418 @param action: Part of error message (see code)
419 @param instances: the instances to check
420 @type instances: list of instance objects
421 @raise errors.OpPrereqError: If conflicting IP addresses are found.
422
423 """
424 conflicts = []
425
426 for instance in instances:
427 instconflicts = [(idx, nic.ip)
428 for (idx, nic) in enumerate(instance.nics)
429 if check_fn(nic)]
430
431 if instconflicts:
432 conflicts.append((instance.name, instconflicts))
433
434 if conflicts:
435 lu.LogWarning("IP addresses from network '%s', which is about to %s"
436 " node group '%s', are in use: %s" %
437 (lu.network_name, action, lu.group.name,
438 utils.CommaJoin(("%s: %s" %
439 (name, _FmtNetworkConflict(details)))
440 for (name, details) in conflicts)))
441
442 raise errors.OpPrereqError("Conflicting IP addresses found; "
443 " remove/modify the corresponding network"
444 " interfaces", errors.ECODE_STATE)
445
446
448 """Connect a network to a nodegroup
449
450 """
451 HPATH = "network-connect"
452 HTYPE = constants.HTYPE_NETWORK
453 REQ_BGL = False
454
456 self.network_name = self.op.network_name
457 self.group_name = self.op.group_name
458 self.network_mode = self.op.network_mode
459 self.network_link = self.op.network_link
460 self.network_vlan = self.op.network_vlan
461
462 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
463 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
464
465 self.needed_locks = {
466 locking.LEVEL_INSTANCE: [],
467 locking.LEVEL_NODEGROUP: [self.group_uuid],
468 }
469 self.share_locks[locking.LEVEL_INSTANCE] = 1
470
471 if self.op.conflicts_check:
472 self.needed_locks[locking.LEVEL_NETWORK] = [self.network_uuid]
473 self.share_locks[locking.LEVEL_NETWORK] = 1
474
485
487 ret = {
488 "GROUP_NAME": self.group_name,
489 "GROUP_NETWORK_MODE": self.network_mode,
490 "GROUP_NETWORK_LINK": self.network_link,
491 "GROUP_NETWORK_VLAN": self.network_vlan,
492 }
493 return ret
494
496 node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
497 return (node_uuids, node_uuids)
498
500 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
501
502 assert self.group_uuid in owned_groups
503
504
505 owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
506 if self.op.conflicts_check:
507 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
508
509 self.netparams = {
510 constants.NIC_MODE: self.network_mode,
511 constants.NIC_LINK: self.network_link,
512 constants.NIC_VLAN: self.network_vlan,
513 }
514
515 objects.NIC.CheckParameterSyntax(self.netparams)
516
517 self.group = self.cfg.GetNodeGroup(self.group_uuid)
518
519
520 self.connected = False
521 if self.network_uuid in self.group.networks:
522 self.LogWarning("Network '%s' is already mapped to group '%s'" %
523 (self.network_name, self.group.name))
524 self.connected = True
525
526
527 elif self.op.conflicts_check:
528 pool = network.AddressPool(self.cfg.GetNetwork(self.network_uuid))
529
530 _NetworkConflictCheck(
531 self, lambda nic: pool.Contains(nic.ip), "connect to",
532 [instance_info for (_, instance_info) in
533 self.cfg.GetMultiInstanceInfoByName(owned_instance_names)])
534
535 - def Exec(self, feedback_fn):
536
537 if not self.connected:
538 self.group.networks[self.network_uuid] = self.netparams
539 self.cfg.Update(self.group, feedback_fn)
540
541
543 """Disconnect a network to a nodegroup
544
545 """
546 HPATH = "network-disconnect"
547 HTYPE = constants.HTYPE_NETWORK
548 REQ_BGL = False
549
551 self.network_name = self.op.network_name
552 self.group_name = self.op.group_name
553
554 self.network_uuid = self.cfg.LookupNetwork(self.network_name)
555 self.group_uuid = self.cfg.LookupNodeGroup(self.group_name)
556
557 self.needed_locks = {
558 locking.LEVEL_INSTANCE: [],
559 locking.LEVEL_NODEGROUP: [self.group_uuid],
560 }
561 self.share_locks[locking.LEVEL_INSTANCE] = 1
562
572
574 ret = {
575 "GROUP_NAME": self.group_name,
576 }
577
578 if self.connected:
579 ret.update({
580 "GROUP_NETWORK_MODE": self.netparams[constants.NIC_MODE],
581 "GROUP_NETWORK_LINK": self.netparams[constants.NIC_LINK],
582 "GROUP_NETWORK_VLAN": self.netparams[constants.NIC_VLAN],
583 })
584 return ret
585
587 nodes = self.cfg.GetNodeGroup(self.group_uuid).members
588 return (nodes, nodes)
589
591 owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
592
593 assert self.group_uuid in owned_groups
594
595
596 owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
597 CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
598
599 self.group = self.cfg.GetNodeGroup(self.group_uuid)
600 self.connected = True
601 if self.network_uuid not in self.group.networks:
602 self.LogWarning("Network '%s' is not mapped to group '%s'",
603 self.network_name, self.group.name)
604 self.connected = False
605
606
607 else:
608 _NetworkConflictCheck(
609 self, lambda nic: nic.network == self.network_uuid, "disconnect from",
610 [instance_info for (_, instance_info) in
611 self.cfg.GetMultiInstanceInfoByName(owned_instances)])
612 self.netparams = self.group.networks.get(self.network_uuid)
613
614 - def Exec(self, feedback_fn):
615
616 if self.connected:
617 del self.group.networks[self.network_uuid]
618 self.cfg.Update(self.group, feedback_fn)
619