Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_utils.py @ a295eb80

History | View | Annotate | Download (18 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
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
36

    
37

    
38
def BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
39
                         minmem, maxmem, vcpus, nics, disk_template, disks,
40
                         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: string
48
  @param primary_node: the name of the instance's primary node
49
  @type secondary_nodes: list
50
  @param secondary_nodes: 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,
84
    "INSTANCE_SECONDARIES": " ".join(secondary_nodes),
85
    "INSTANCE_OS_TYPE": os_type,
86
    "INSTANCE_STATUS": status,
87
    "INSTANCE_MINMEM": minmem,
88
    "INSTANCE_MAXMEM": maxmem,
89
    # TODO(2.9) remove deprecated "memory" value
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, _, ip, mac, mode, link, net, netinfo) in enumerate(nics):
98
      if ip is None:
99
        ip = ""
100
      env["INSTANCE_NIC%d_NAME" % idx] = name
101
      env["INSTANCE_NIC%d_IP" % idx] = ip
102
      env["INSTANCE_NIC%d_MAC" % idx] = mac
103
      env["INSTANCE_NIC%d_MODE" % idx] = mode
104
      env["INSTANCE_NIC%d_LINK" % idx] = link
105
      if netinfo:
106
        nobj = objects.Network.FromDict(netinfo)
107
        env.update(nobj.HooksDict("INSTANCE_NIC%d_" % idx))
108
      elif network:
109
        # FIXME: broken network reference: the instance NIC specifies a
110
        # network, but the relevant network entry was not in the config. This
111
        # should be made impossible.
112
        env["INSTANCE_NIC%d_NETWORK_NAME" % idx] = net
113
      if mode == constants.NIC_MODE_BRIDGED:
114
        env["INSTANCE_NIC%d_BRIDGE" % idx] = link
115
  else:
116
    nic_count = 0
117

    
118
  env["INSTANCE_NIC_COUNT"] = nic_count
119

    
120
  if disks:
121
    disk_count = len(disks)
122
    for idx, (name, size, mode) in enumerate(disks):
123
      env["INSTANCE_DISK%d_NAME" % idx] = name
124
      env["INSTANCE_DISK%d_SIZE" % idx] = size
125
      env["INSTANCE_DISK%d_MODE" % idx] = mode
126
  else:
127
    disk_count = 0
128

    
129
  env["INSTANCE_DISK_COUNT"] = disk_count
130

    
131
  if not tags:
132
    tags = []
133

    
134
  env["INSTANCE_TAGS"] = " ".join(tags)
135

    
136
  for source, kind in [(bep, "BE"), (hvp, "HV")]:
137
    for key, value in source.items():
138
      env["INSTANCE_%s_%s" % (kind, key)] = value
139

    
140
  return env
141

    
142

    
143
def BuildInstanceHookEnvByObject(lu, instance, override=None):
144
  """Builds instance related env variables for hooks from an object.
145

146
  @type lu: L{LogicalUnit}
147
  @param lu: the logical unit on whose behalf we execute
148
  @type instance: L{objects.Instance}
149
  @param instance: the instance for which we should build the
150
      environment
151
  @type override: dict
152
  @param override: dictionary with key/values that will override
153
      our values
154
  @rtype: dict
155
  @return: the hook environment dictionary
156

157
  """
158
  cluster = lu.cfg.GetClusterInfo()
159
  bep = cluster.FillBE(instance)
160
  hvp = cluster.FillHV(instance)
161
  args = {
162
    "name": instance.name,
163
    "primary_node": instance.primary_node,
164
    "secondary_nodes": instance.secondary_nodes,
165
    "os_type": instance.os,
166
    "status": instance.admin_state,
167
    "maxmem": bep[constants.BE_MAXMEM],
168
    "minmem": bep[constants.BE_MINMEM],
169
    "vcpus": bep[constants.BE_VCPUS],
170
    "nics": NICListToTuple(lu, instance.nics),
171
    "disk_template": instance.disk_template,
172
    "disks": [(disk.name, disk.size, disk.mode)
173
              for disk in instance.disks],
174
    "bep": bep,
175
    "hvp": hvp,
176
    "hypervisor_name": instance.hypervisor,
177
    "tags": instance.tags,
178
  }
179
  if override:
180
    args.update(override)
181
  return BuildInstanceHookEnv(**args) # pylint: disable=W0142
182

    
183

    
184
def GetClusterDomainSecret():
185
  """Reads the cluster domain secret.
186

187
  """
188
  return utils.ReadOneLineFile(pathutils.CLUSTER_DOMAIN_SECRET_FILE,
189
                               strict=True)
190

    
191

    
192
def CheckNodeNotDrained(lu, node):
193
  """Ensure that a given node is not drained.
194

195
  @param lu: the LU on behalf of which we make the check
196
  @param node: the node to check
197
  @raise errors.OpPrereqError: if the node is drained
198

199
  """
200
  if lu.cfg.GetNodeInfo(node).drained:
201
    raise errors.OpPrereqError("Can't use drained node %s" % node,
202
                               errors.ECODE_STATE)
203

    
204

    
205
def CheckNodeVmCapable(lu, node):
206
  """Ensure that a given node is vm capable.
207

208
  @param lu: the LU on behalf of which we make the check
209
  @param node: the node to check
210
  @raise errors.OpPrereqError: if the node is not vm capable
211

212
  """
213
  if not lu.cfg.GetNodeInfo(node).vm_capable:
214
    raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node,
215
                               errors.ECODE_STATE)
216

    
217

    
218
def RemoveInstance(lu, feedback_fn, instance, ignore_failures):
219
  """Utility function to remove an instance.
220

221
  """
222
  logging.info("Removing block devices for instance %s", instance.name)
223

    
224
  if not RemoveDisks(lu, instance, ignore_failures=ignore_failures):
225
    if not ignore_failures:
226
      raise errors.OpExecError("Can't remove instance's disks")
227
    feedback_fn("Warning: can't remove instance's disks")
228

    
229
  logging.info("Removing instance %s out of cluster config", instance.name)
230

    
231
  lu.cfg.RemoveInstance(instance.name)
232

    
233
  assert not lu.remove_locks.get(locking.LEVEL_INSTANCE), \
234
    "Instance lock removal conflict"
235

    
236
  # Remove lock for the instance
237
  lu.remove_locks[locking.LEVEL_INSTANCE] = instance.name
238

    
239

    
240
def RemoveDisks(lu, instance, target_node=None, ignore_failures=False):
241
  """Remove all disks for an instance.
242

243
  This abstracts away some work from `AddInstance()` and
244
  `RemoveInstance()`. Note that in case some of the devices couldn't
245
  be removed, the removal will continue with the other ones.
246

247
  @type lu: L{LogicalUnit}
248
  @param lu: the logical unit on whose behalf we execute
249
  @type instance: L{objects.Instance}
250
  @param instance: the instance whose disks we should remove
251
  @type target_node: string
252
  @param target_node: used to override the node on which to remove the disks
253
  @rtype: boolean
254
  @return: the success of the removal
255

256
  """
257
  logging.info("Removing block devices for instance %s", instance.name)
258

    
259
  all_result = True
260
  ports_to_release = set()
261
  anno_disks = AnnotateDiskParams(instance, instance.disks, lu.cfg)
262
  for (idx, device) in enumerate(anno_disks):
263
    if target_node:
264
      edata = [(target_node, device)]
265
    else:
266
      edata = device.ComputeNodeTree(instance.primary_node)
267
    for node, disk in edata:
268
      lu.cfg.SetDiskID(disk, node)
269
      result = lu.rpc.call_blockdev_remove(node, disk)
270
      if result.fail_msg:
271
        lu.LogWarning("Could not remove disk %s on node %s,"
272
                      " continuing anyway: %s", idx, node, result.fail_msg)
273
        if not (result.offline and node != instance.primary_node):
274
          all_result = False
275

    
276
    # if this is a DRBD disk, return its port to the pool
277
    if device.dev_type in constants.LDS_DRBD:
278
      ports_to_release.add(device.logical_id[2])
279

    
280
  if all_result or ignore_failures:
281
    for port in ports_to_release:
282
      lu.cfg.AddTcpUdpPort(port)
283

    
284
  if instance.disk_template in constants.DTS_FILEBASED:
285
    file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
286
    if target_node:
287
      tgt = target_node
288
    else:
289
      tgt = instance.primary_node
290
    result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
291
    if result.fail_msg:
292
      lu.LogWarning("Could not remove directory '%s' on node %s: %s",
293
                    file_storage_dir, instance.primary_node, result.fail_msg)
294
      all_result = False
295

    
296
  return all_result
297

    
298

    
299
def NICToTuple(lu, nic):
300
  """Build a tupple of nic information.
301

302
  @type lu:  L{LogicalUnit}
303
  @param lu: the logical unit on whose behalf we execute
304
  @type nic: L{objects.NIC}
305
  @param nic: nic to convert to hooks tuple
306

307
  """
308
  cluster = lu.cfg.GetClusterInfo()
309
  filled_params = cluster.SimpleFillNIC(nic.nicparams)
310
  mode = filled_params[constants.NIC_MODE]
311
  link = filled_params[constants.NIC_LINK]
312
  netinfo = None
313
  if nic.network:
314
    nobj = lu.cfg.GetNetwork(nic.network)
315
    netinfo = objects.Network.ToDict(nobj)
316
  return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, nic.network, netinfo)
317

    
318

    
319
def NICListToTuple(lu, nics):
320
  """Build a list of nic information tuples.
321

322
  This list is suitable to be passed to _BuildInstanceHookEnv or as a return
323
  value in LUInstanceQueryData.
324

325
  @type lu:  L{LogicalUnit}
326
  @param lu: the logical unit on whose behalf we execute
327
  @type nics: list of L{objects.NIC}
328
  @param nics: list of nics to convert to hooks tuples
329

330
  """
331
  hooks_nics = []
332
  for nic in nics:
333
    hooks_nics.append(NICToTuple(lu, nic))
334
  return hooks_nics
335

    
336

    
337
def CopyLockList(names):
338
  """Makes a copy of a list of lock names.
339

340
  Handles L{locking.ALL_SET} correctly.
341

342
  """
343
  if names == locking.ALL_SET:
344
    return locking.ALL_SET
345
  else:
346
    return names[:]
347

    
348

    
349
def ReleaseLocks(lu, level, names=None, keep=None):
350
  """Releases locks owned by an LU.
351

352
  @type lu: L{LogicalUnit}
353
  @param level: Lock level
354
  @type names: list or None
355
  @param names: Names of locks to release
356
  @type keep: list or None
357
  @param keep: Names of locks to retain
358

359
  """
360
  assert not (keep is not None and names is not None), \
361
         "Only one of the 'names' and the 'keep' parameters can be given"
362

    
363
  if names is not None:
364
    should_release = names.__contains__
365
  elif keep:
366
    should_release = lambda name: name not in keep
367
  else:
368
    should_release = None
369

    
370
  owned = lu.owned_locks(level)
371
  if not owned:
372
    # Not owning any lock at this level, do nothing
373
    pass
374

    
375
  elif should_release:
376
    retain = []
377
    release = []
378

    
379
    # Determine which locks to release
380
    for name in owned:
381
      if should_release(name):
382
        release.append(name)
383
      else:
384
        retain.append(name)
385

    
386
    assert len(lu.owned_locks(level)) == (len(retain) + len(release))
387

    
388
    # Release just some locks
389
    lu.glm.release(level, names=release)
390

    
391
    assert frozenset(lu.owned_locks(level)) == frozenset(retain)
392
  else:
393
    # Release everything
394
    lu.glm.release(level)
395

    
396
    assert not lu.glm.is_owned(level), "No locks should be owned"
397

    
398

    
399
def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group,
400
                                 target_group, cfg,
401
                                 _compute_fn=ComputeIPolicyInstanceViolation):
402
  """Compute if instance meets the specs of the new target group.
403

404
  @param ipolicy: The ipolicy to verify
405
  @param instance: The instance object to verify
406
  @param current_group: The current group of the instance
407
  @param target_group: The new group of the instance
408
  @type cfg: L{config.ConfigWriter}
409
  @param cfg: Cluster configuration
410
  @param _compute_fn: The function to verify ipolicy (unittest only)
411
  @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
412

413
  """
414
  if current_group == target_group:
415
    return []
416
  else:
417
    return _compute_fn(ipolicy, instance, cfg)
418

    
419

    
420
def CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False,
421
                           _compute_fn=_ComputeIPolicyNodeViolation):
422
  """Checks that the target node is correct in terms of instance policy.
423

424
  @param ipolicy: The ipolicy to verify
425
  @param instance: The instance object to verify
426
  @param node: The new node to relocate
427
  @type cfg: L{config.ConfigWriter}
428
  @param cfg: Cluster configuration
429
  @param ignore: Ignore violations of the ipolicy
430
  @param _compute_fn: The function to verify ipolicy (unittest only)
431
  @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
432

433
  """
434
  primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
435
  res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
436

    
437
  if res:
438
    msg = ("Instance does not meet target node group's (%s) instance"
439
           " policy: %s") % (node.group, utils.CommaJoin(res))
440
    if ignore:
441
      lu.LogWarning(msg)
442
    else:
443
      raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
444

    
445

    
446
def GetInstanceInfoText(instance):
447
  """Compute that text that should be added to the disk's metadata.
448

449
  """
450
  return "originstname+%s" % instance.name
451

    
452

    
453
def CheckNodeFreeMemory(lu, node, reason, requested, hvname, hvparams):
454
  """Checks if a node has enough free memory.
455

456
  This function checks if a given node has the needed amount of free
457
  memory. In case the node has less memory or we cannot get the
458
  information from the node, this function raises an OpPrereqError
459
  exception.
460

461
  @type lu: C{LogicalUnit}
462
  @param lu: a logical unit from which we get configuration data
463
  @type node: C{str}
464
  @param node: the node to check
465
  @type reason: C{str}
466
  @param reason: string to use in the error message
467
  @type requested: C{int}
468
  @param requested: the amount of memory in MiB to check for
469
  @type hvname: string
470
  @param hvname: the hypervisor's name
471
  @type hvparams: dict of strings
472
  @param hvparams: the hypervisor's parameters
473
  @rtype: integer
474
  @return: node current free memory
475
  @raise errors.OpPrereqError: if the node doesn't have enough memory, or
476
      we cannot check the node
477

478
  """
479
  nodeinfo = lu.rpc.call_node_info([node], None, [(hvname, hvparams)], False)
480
  nodeinfo[node].Raise("Can't get data from node %s" % node,
481
                       prereq=True, ecode=errors.ECODE_ENVIRON)
482
  (_, _, (hv_info, )) = nodeinfo[node].payload
483

    
484
  free_mem = hv_info.get("memory_free", None)
485
  if not isinstance(free_mem, int):
486
    raise errors.OpPrereqError("Can't compute free memory on node %s, result"
487
                               " was '%s'" % (node, free_mem),
488
                               errors.ECODE_ENVIRON)
489
  if requested > free_mem:
490
    raise errors.OpPrereqError("Not enough memory on node %s for %s:"
491
                               " needed %s MiB, available %s MiB" %
492
                               (node, reason, requested, free_mem),
493
                               errors.ECODE_NORES)
494
  return free_mem
495

    
496

    
497
def CheckInstanceBridgesExist(lu, instance, node=None):
498
  """Check that the brigdes needed by an instance exist.
499

500
  """
501
  if node is None:
502
    node = instance.primary_node
503
  CheckNicsBridgesExist(lu, instance.nics, node)
504

    
505

    
506
def CheckNicsBridgesExist(lu, target_nics, target_node):
507
  """Check that the brigdes needed by a list of nics exist.
508

509
  """
510
  cluster = lu.cfg.GetClusterInfo()
511
  paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in target_nics]
512
  brlist = [params[constants.NIC_LINK] for params in paramslist
513
            if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
514
  if brlist:
515
    result = lu.rpc.call_bridges_exist(target_node, brlist)
516
    result.Raise("Error checking bridges on destination node '%s'" %
517
                 target_node, prereq=True, ecode=errors.ECODE_ENVIRON)
518

    
519

    
520
def CheckNodeHasOS(lu, node, os_name, force_variant):
521
  """Ensure that a node supports a given OS.
522

523
  @param lu: the LU on behalf of which we make the check
524
  @param node: the node to check
525
  @param os_name: the OS to query about
526
  @param force_variant: whether to ignore variant errors
527
  @raise errors.OpPrereqError: if the node is not supporting the OS
528

529
  """
530
  result = lu.rpc.call_os_get(node, os_name)
531
  result.Raise("OS '%s' not in supported OS list for node %s" %
532
               (os_name, node),
533
               prereq=True, ecode=errors.ECODE_INVAL)
534
  if not force_variant:
535
    _CheckOSVariant(result.payload, os_name)
536

    
537

    
538
def _CheckOSVariant(os_obj, name):
539
  """Check whether an OS name conforms to the os variants specification.
540

541
  @type os_obj: L{objects.OS}
542
  @param os_obj: OS object to check
543
  @type name: string
544
  @param name: OS name passed by the user, to check for validity
545

546
  """
547
  variant = objects.OS.GetVariant(name)
548
  if not os_obj.supported_variants:
549
    if variant:
550
      raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
551
                                 " passed)" % (os_obj.name, variant),
552
                                 errors.ECODE_INVAL)
553
    return
554
  if not variant:
555
    raise errors.OpPrereqError("OS name must include a variant",
556
                               errors.ECODE_INVAL)
557

    
558
  if variant not in os_obj.supported_variants:
559
    raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)