1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Utility function mainly, but not only used by instance LU's."""
23
24 import logging
25 import os
26
27 from ganeti import constants
28 from ganeti import errors
29 from ganeti import locking
30 from ganeti import network
31 from ganeti import objects
32 from ganeti import pathutils
33 from ganeti import utils
34 from ganeti.cmdlib.common import AnnotateDiskParams, \
35 ComputeIPolicyInstanceViolation, CheckDiskTemplateEnabled
36
37
38 -def BuildInstanceHookEnv(name, primary_node_name, secondary_node_names, os_type,
39 status, minmem, maxmem, vcpus, nics, disk_template,
40 disks, bep, hvp, hypervisor_name, tags):
41 """Builds instance related env variables for hooks
42
43 This builds the hook environment from individual variables.
44
45 @type name: string
46 @param name: the name of the instance
47 @type primary_node_name: string
48 @param primary_node_name: the name of the instance's primary node
49 @type secondary_node_names: list
50 @param secondary_node_names: list of secondary nodes as strings
51 @type os_type: string
52 @param os_type: the name of the instance's OS
53 @type status: string
54 @param status: the desired status of the instance
55 @type minmem: string
56 @param minmem: the minimum memory size of the instance
57 @type maxmem: string
58 @param maxmem: the maximum memory size of the instance
59 @type vcpus: string
60 @param vcpus: the count of VCPUs the instance has
61 @type nics: list
62 @param nics: list of tuples (name, uuid, ip, mac, mode, link, net, netinfo)
63 representing the NICs the instance has
64 @type disk_template: string
65 @param disk_template: the disk template of the instance
66 @type disks: list
67 @param disks: list of tuples (name, uuid, size, mode)
68 @type bep: dict
69 @param bep: the backend parameters for the instance
70 @type hvp: dict
71 @param hvp: the hypervisor parameters for the instance
72 @type hypervisor_name: string
73 @param hypervisor_name: the hypervisor for the instance
74 @type tags: list
75 @param tags: list of instance tags as strings
76 @rtype: dict
77 @return: the hook environment for this instance
78
79 """
80 env = {
81 "OP_TARGET": name,
82 "INSTANCE_NAME": name,
83 "INSTANCE_PRIMARY": primary_node_name,
84 "INSTANCE_SECONDARIES": " ".join(secondary_node_names),
85 "INSTANCE_OS_TYPE": os_type,
86 "INSTANCE_STATUS": status,
87 "INSTANCE_MINMEM": minmem,
88 "INSTANCE_MAXMEM": maxmem,
89
90 "INSTANCE_MEMORY": maxmem,
91 "INSTANCE_VCPUS": vcpus,
92 "INSTANCE_DISK_TEMPLATE": disk_template,
93 "INSTANCE_HYPERVISOR": hypervisor_name,
94 }
95 if nics:
96 nic_count = len(nics)
97 for idx, (name, uuid, ip, mac, mode, link, net, netinfo) in enumerate(nics):
98 if ip is None:
99 ip = ""
100 if name:
101 env["INSTANCE_NIC%d_NAME" % idx] = name
102 env["INSTANCE_NIC%d_UUID" % idx] = uuid
103 env["INSTANCE_NIC%d_IP" % idx] = ip
104 env["INSTANCE_NIC%d_MAC" % idx] = mac
105 env["INSTANCE_NIC%d_MODE" % idx] = mode
106 env["INSTANCE_NIC%d_LINK" % idx] = link
107 if netinfo:
108 nobj = objects.Network.FromDict(netinfo)
109 env.update(nobj.HooksDict("INSTANCE_NIC%d_" % idx))
110 elif network:
111
112
113
114 env["INSTANCE_NIC%d_NETWORK_NAME" % idx] = net
115 if mode == constants.NIC_MODE_BRIDGED:
116 env["INSTANCE_NIC%d_BRIDGE" % idx] = link
117 else:
118 nic_count = 0
119
120 env["INSTANCE_NIC_COUNT"] = nic_count
121
122 if disks:
123 disk_count = len(disks)
124 for idx, (name, uuid, size, mode) in enumerate(disks):
125 if name:
126 env["INSTANCE_DISK%d_NAME" % idx] = name
127 env["INSTANCE_DISK%d_UUID" % idx] = uuid
128 env["INSTANCE_DISK%d_SIZE" % idx] = size
129 env["INSTANCE_DISK%d_MODE" % idx] = mode
130 else:
131 disk_count = 0
132
133 env["INSTANCE_DISK_COUNT"] = disk_count
134
135 if not tags:
136 tags = []
137
138 env["INSTANCE_TAGS"] = " ".join(tags)
139
140 for source, kind in [(bep, "BE"), (hvp, "HV")]:
141 for key, value in source.items():
142 env["INSTANCE_%s_%s" % (kind, key)] = value
143
144 return env
145
146
148 """Builds instance related env variables for hooks from an object.
149
150 @type lu: L{LogicalUnit}
151 @param lu: the logical unit on whose behalf we execute
152 @type instance: L{objects.Instance}
153 @param instance: the instance for which we should build the
154 environment
155 @type override: dict
156 @param override: dictionary with key/values that will override
157 our values
158 @rtype: dict
159 @return: the hook environment dictionary
160
161 """
162 cluster = lu.cfg.GetClusterInfo()
163 bep = cluster.FillBE(instance)
164 hvp = cluster.FillHV(instance)
165 args = {
166 "name": instance.name,
167 "primary_node_name": lu.cfg.GetNodeName(instance.primary_node),
168 "secondary_node_names": lu.cfg.GetNodeNames(instance.secondary_nodes),
169 "os_type": instance.os,
170 "status": instance.admin_state,
171 "maxmem": bep[constants.BE_MAXMEM],
172 "minmem": bep[constants.BE_MINMEM],
173 "vcpus": bep[constants.BE_VCPUS],
174 "nics": NICListToTuple(lu, instance.nics),
175 "disk_template": instance.disk_template,
176 "disks": [(disk.name, disk.uuid, disk.size, disk.mode)
177 for disk in instance.disks],
178 "bep": bep,
179 "hvp": hvp,
180 "hypervisor_name": instance.hypervisor,
181 "tags": instance.tags,
182 }
183 if override:
184 args.update(override)
185 return BuildInstanceHookEnv(**args)
186
187
189 """Reads the cluster domain secret.
190
191 """
192 return utils.ReadOneLineFile(pathutils.CLUSTER_DOMAIN_SECRET_FILE,
193 strict=True)
194
195
197 """Ensure that a given node is not drained.
198
199 @param lu: the LU on behalf of which we make the check
200 @param node_uuid: the node to check
201 @raise errors.OpPrereqError: if the node is drained
202
203 """
204 node = lu.cfg.GetNodeInfo(node_uuid)
205 if node.drained:
206 raise errors.OpPrereqError("Can't use drained node %s" % node.name,
207 errors.ECODE_STATE)
208
209
211 """Ensure that a given node is vm capable.
212
213 @param lu: the LU on behalf of which we make the check
214 @param node_uuid: the node to check
215 @raise errors.OpPrereqError: if the node is not vm capable
216
217 """
218 if not lu.cfg.GetNodeInfo(node_uuid).vm_capable:
219 raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node_uuid,
220 errors.ECODE_STATE)
221
222
224 """Utility function to remove an instance.
225
226 """
227 logging.info("Removing block devices for instance %s", instance.name)
228
229 if not RemoveDisks(lu, instance, ignore_failures=ignore_failures):
230 if not ignore_failures:
231 raise errors.OpExecError("Can't remove instance's disks")
232 feedback_fn("Warning: can't remove instance's disks")
233
234 logging.info("Removing instance %s out of cluster config", instance.name)
235
236 lu.cfg.RemoveInstance(instance.uuid)
237
238 assert not lu.remove_locks.get(locking.LEVEL_INSTANCE), \
239 "Instance lock removal conflict"
240
241
242 lu.remove_locks[locking.LEVEL_INSTANCE] = instance.name
243
244
245 -def RemoveDisks(lu, instance, target_node_uuid=None, ignore_failures=False):
246 """Remove all disks for an instance.
247
248 This abstracts away some work from `AddInstance()` and
249 `RemoveInstance()`. Note that in case some of the devices couldn't
250 be removed, the removal will continue with the other ones.
251
252 @type lu: L{LogicalUnit}
253 @param lu: the logical unit on whose behalf we execute
254 @type instance: L{objects.Instance}
255 @param instance: the instance whose disks we should remove
256 @type target_node_uuid: string
257 @param target_node_uuid: used to override the node on which to remove the
258 disks
259 @rtype: boolean
260 @return: the success of the removal
261
262 """
263 logging.info("Removing block devices for instance %s", instance.name)
264
265 all_result = True
266 ports_to_release = set()
267 anno_disks = AnnotateDiskParams(instance, instance.disks, lu.cfg)
268 for (idx, device) in enumerate(anno_disks):
269 if target_node_uuid:
270 edata = [(target_node_uuid, device)]
271 else:
272 edata = device.ComputeNodeTree(instance.primary_node)
273 for node_uuid, disk in edata:
274 lu.cfg.SetDiskID(disk, node_uuid)
275 result = lu.rpc.call_blockdev_remove(node_uuid, disk)
276 if result.fail_msg:
277 lu.LogWarning("Could not remove disk %s on node %s,"
278 " continuing anyway: %s", idx,
279 lu.cfg.GetNodeName(node_uuid), result.fail_msg)
280 if not (result.offline and node_uuid != instance.primary_node):
281 all_result = False
282
283
284 if device.dev_type in constants.DTS_DRBD:
285 ports_to_release.add(device.logical_id[2])
286
287 if all_result or ignore_failures:
288 for port in ports_to_release:
289 lu.cfg.AddTcpUdpPort(port)
290
291 CheckDiskTemplateEnabled(lu.cfg.GetClusterInfo(), instance.disk_template)
292
293 if instance.disk_template in constants.DTS_FILEBASED:
294 if len(instance.disks) > 0:
295 file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
296 else:
297 if instance.disk_template == constants.DT_SHARED_FILE:
298 file_storage_dir = utils.PathJoin(lu.cfg.GetSharedFileStorageDir(),
299 instance.name)
300 else:
301 file_storage_dir = utils.PathJoin(lu.cfg.GetFileStorageDir(),
302 instance.name)
303 if target_node_uuid:
304 tgt = target_node_uuid
305 else:
306 tgt = instance.primary_node
307 result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
308 if result.fail_msg:
309 lu.LogWarning("Could not remove directory '%s' on node %s: %s",
310 file_storage_dir, lu.cfg.GetNodeName(tgt), result.fail_msg)
311 all_result = False
312
313 return all_result
314
315
334
335
337 """Build a list of nic information tuples.
338
339 This list is suitable to be passed to _BuildInstanceHookEnv or as a return
340 value in LUInstanceQueryData.
341
342 @type lu: L{LogicalUnit}
343 @param lu: the logical unit on whose behalf we execute
344 @type nics: list of L{objects.NIC}
345 @param nics: list of nics to convert to hooks tuples
346
347 """
348 hooks_nics = []
349 for nic in nics:
350 hooks_nics.append(NICToTuple(lu, nic))
351 return hooks_nics
352
353
355 """Makes a copy of a list of lock names.
356
357 Handles L{locking.ALL_SET} correctly.
358
359 """
360 if names == locking.ALL_SET:
361 return locking.ALL_SET
362 else:
363 return names[:]
364
365
367 """Releases locks owned by an LU.
368
369 @type lu: L{LogicalUnit}
370 @param level: Lock level
371 @type names: list or None
372 @param names: Names of locks to release
373 @type keep: list or None
374 @param keep: Names of locks to retain
375
376 """
377 assert not (keep is not None and names is not None), \
378 "Only one of the 'names' and the 'keep' parameters can be given"
379
380 if names is not None:
381 should_release = names.__contains__
382 elif keep:
383 should_release = lambda name: name not in keep
384 else:
385 should_release = None
386
387 owned = lu.owned_locks(level)
388 if not owned:
389
390 pass
391
392 elif should_release:
393 retain = []
394 release = []
395
396
397 for name in owned:
398 if should_release(name):
399 release.append(name)
400 else:
401 retain.append(name)
402
403 assert len(lu.owned_locks(level)) == (len(retain) + len(release))
404
405
406 lu.glm.release(level, names=release)
407
408 assert frozenset(lu.owned_locks(level)) == frozenset(retain)
409 else:
410
411 lu.glm.release(level)
412
413 assert not lu.glm.is_owned(level), "No locks should be owned"
414
415
419 """Compute if instance meets the specs of the new target group.
420
421 @param ipolicy: The ipolicy to verify
422 @param instance: The instance object to verify
423 @param current_group: The current group of the instance
424 @param target_group: The new group of the instance
425 @type cfg: L{config.ConfigWriter}
426 @param cfg: Cluster configuration
427 @param _compute_fn: The function to verify ipolicy (unittest only)
428 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
429
430 """
431 if current_group == target_group:
432 return []
433 else:
434 return _compute_fn(ipolicy, instance, cfg)
435
436
439 """Checks that the target node is correct in terms of instance policy.
440
441 @param ipolicy: The ipolicy to verify
442 @param instance: The instance object to verify
443 @param node: The new node to relocate
444 @type cfg: L{config.ConfigWriter}
445 @param cfg: Cluster configuration
446 @param ignore: Ignore violations of the ipolicy
447 @param _compute_fn: The function to verify ipolicy (unittest only)
448 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
449
450 """
451 primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
452 res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
453
454 if res:
455 msg = ("Instance does not meet target node group's (%s) instance"
456 " policy: %s") % (node.group, utils.CommaJoin(res))
457 if ignore:
458 lu.LogWarning(msg)
459 else:
460 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
461
462
464 """Compute that text that should be added to the disk's metadata.
465
466 """
467 return "originstname+%s" % instance.name
468
469
471 """Checks if a node has enough free memory.
472
473 This function checks if a given node has the needed amount of free
474 memory. In case the node has less memory or we cannot get the
475 information from the node, this function raises an OpPrereqError
476 exception.
477
478 @type lu: C{LogicalUnit}
479 @param lu: a logical unit from which we get configuration data
480 @type node_uuid: C{str}
481 @param node_uuid: the node to check
482 @type reason: C{str}
483 @param reason: string to use in the error message
484 @type requested: C{int}
485 @param requested: the amount of memory in MiB to check for
486 @type hvname: string
487 @param hvname: the hypervisor's name
488 @type hvparams: dict of strings
489 @param hvparams: the hypervisor's parameters
490 @rtype: integer
491 @return: node current free memory
492 @raise errors.OpPrereqError: if the node doesn't have enough memory, or
493 we cannot check the node
494
495 """
496 node_name = lu.cfg.GetNodeName(node_uuid)
497 nodeinfo = lu.rpc.call_node_info([node_uuid], None, [(hvname, hvparams)])
498 nodeinfo[node_uuid].Raise("Can't get data from node %s" % node_name,
499 prereq=True, ecode=errors.ECODE_ENVIRON)
500 (_, _, (hv_info, )) = nodeinfo[node_uuid].payload
501
502 free_mem = hv_info.get("memory_free", None)
503 if not isinstance(free_mem, int):
504 raise errors.OpPrereqError("Can't compute free memory on node %s, result"
505 " was '%s'" % (node_name, free_mem),
506 errors.ECODE_ENVIRON)
507 if requested > free_mem:
508 raise errors.OpPrereqError("Not enough memory on node %s for %s:"
509 " needed %s MiB, available %s MiB" %
510 (node_name, reason, requested, free_mem),
511 errors.ECODE_NORES)
512 return free_mem
513
514
516 """Check that the brigdes needed by an instance exist.
517
518 """
519 if node_uuid is None:
520 node_uuid = instance.primary_node
521 CheckNicsBridgesExist(lu, instance.nics, node_uuid)
522
523
537
538
540 """Ensure that a node supports a given OS.
541
542 @param lu: the LU on behalf of which we make the check
543 @param node_uuid: the node to check
544 @param os_name: the OS to query about
545 @param force_variant: whether to ignore variant errors
546 @raise errors.OpPrereqError: if the node is not supporting the OS
547
548 """
549 result = lu.rpc.call_os_get(node_uuid, os_name)
550 result.Raise("OS '%s' not in supported OS list for node %s" %
551 (os_name, lu.cfg.GetNodeName(node_uuid)),
552 prereq=True, ecode=errors.ECODE_INVAL)
553 if not force_variant:
554 _CheckOSVariant(result.payload, os_name)
555
556
558 """Check whether an OS name conforms to the os variants specification.
559
560 @type os_obj: L{objects.OS}
561 @param os_obj: OS object to check
562 @type name: string
563 @param name: OS name passed by the user, to check for validity
564
565 """
566 variant = objects.OS.GetVariant(name)
567 if not os_obj.supported_variants:
568 if variant:
569 raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
570 " passed)" % (os_obj.name, variant),
571 errors.ECODE_INVAL)
572 return
573 if not variant:
574 raise errors.OpPrereqError("OS name must include a variant",
575 errors.ECODE_INVAL)
576
577 if variant not in os_obj.supported_variants:
578 raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
579