Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_utils.py @ 809a055b

History | View | Annotate | Download (20.4 kB)

1 22b7f6f8 Thomas Thrainer
#
2 22b7f6f8 Thomas Thrainer
#
3 22b7f6f8 Thomas Thrainer
4 0ef72034 Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
5 22b7f6f8 Thomas Thrainer
#
6 22b7f6f8 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 22b7f6f8 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 22b7f6f8 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 22b7f6f8 Thomas Thrainer
# (at your option) any later version.
10 22b7f6f8 Thomas Thrainer
#
11 22b7f6f8 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 22b7f6f8 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 22b7f6f8 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 22b7f6f8 Thomas Thrainer
# General Public License for more details.
15 22b7f6f8 Thomas Thrainer
#
16 22b7f6f8 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 22b7f6f8 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 22b7f6f8 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 22b7f6f8 Thomas Thrainer
# 02110-1301, USA.
20 22b7f6f8 Thomas Thrainer
21 22b7f6f8 Thomas Thrainer
22 22b7f6f8 Thomas Thrainer
"""Utility function mainly, but not only used by instance LU's."""
23 22b7f6f8 Thomas Thrainer
24 22b7f6f8 Thomas Thrainer
import logging
25 22b7f6f8 Thomas Thrainer
import os
26 22b7f6f8 Thomas Thrainer
27 22b7f6f8 Thomas Thrainer
from ganeti import constants
28 22b7f6f8 Thomas Thrainer
from ganeti import errors
29 22b7f6f8 Thomas Thrainer
from ganeti import locking
30 22b7f6f8 Thomas Thrainer
from ganeti import network
31 22b7f6f8 Thomas Thrainer
from ganeti import objects
32 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
33 22b7f6f8 Thomas Thrainer
from ganeti import utils
34 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import AnnotateDiskParams, \
35 1f7c8208 Helga Velroyen
  ComputeIPolicyInstanceViolation, CheckDiskTemplateEnabled
36 22b7f6f8 Thomas Thrainer
37 22b7f6f8 Thomas Thrainer
38 1c3231aa Thomas Thrainer
def BuildInstanceHookEnv(name, primary_node_name, secondary_node_names, os_type,
39 1c3231aa Thomas Thrainer
                         status, minmem, maxmem, vcpus, nics, disk_template,
40 1c3231aa Thomas Thrainer
                         disks, bep, hvp, hypervisor_name, tags):
41 22b7f6f8 Thomas Thrainer
  """Builds instance related env variables for hooks
42 22b7f6f8 Thomas Thrainer

43 22b7f6f8 Thomas Thrainer
  This builds the hook environment from individual variables.
44 22b7f6f8 Thomas Thrainer

45 22b7f6f8 Thomas Thrainer
  @type name: string
46 22b7f6f8 Thomas Thrainer
  @param name: the name of the instance
47 1c3231aa Thomas Thrainer
  @type primary_node_name: string
48 1c3231aa Thomas Thrainer
  @param primary_node_name: the name of the instance's primary node
49 1c3231aa Thomas Thrainer
  @type secondary_node_names: list
50 1c3231aa Thomas Thrainer
  @param secondary_node_names: list of secondary nodes as strings
51 22b7f6f8 Thomas Thrainer
  @type os_type: string
52 22b7f6f8 Thomas Thrainer
  @param os_type: the name of the instance's OS
53 22b7f6f8 Thomas Thrainer
  @type status: string
54 22b7f6f8 Thomas Thrainer
  @param status: the desired status of the instance
55 22b7f6f8 Thomas Thrainer
  @type minmem: string
56 22b7f6f8 Thomas Thrainer
  @param minmem: the minimum memory size of the instance
57 22b7f6f8 Thomas Thrainer
  @type maxmem: string
58 22b7f6f8 Thomas Thrainer
  @param maxmem: the maximum memory size of the instance
59 22b7f6f8 Thomas Thrainer
  @type vcpus: string
60 22b7f6f8 Thomas Thrainer
  @param vcpus: the count of VCPUs the instance has
61 22b7f6f8 Thomas Thrainer
  @type nics: list
62 d676dbea Sebastian Gebhard
  @param nics: list of tuples (name, uuid, ip, mac, mode, link, vlan, net,
63 d676dbea Sebastian Gebhard
      netinfo) representing the NICs the instance has
64 22b7f6f8 Thomas Thrainer
  @type disk_template: string
65 22b7f6f8 Thomas Thrainer
  @param disk_template: the disk template of the instance
66 22b7f6f8 Thomas Thrainer
  @type disks: list
67 22b7f6f8 Thomas Thrainer
  @param disks: list of tuples (name, uuid, size, mode)
68 22b7f6f8 Thomas Thrainer
  @type bep: dict
69 22b7f6f8 Thomas Thrainer
  @param bep: the backend parameters for the instance
70 22b7f6f8 Thomas Thrainer
  @type hvp: dict
71 22b7f6f8 Thomas Thrainer
  @param hvp: the hypervisor parameters for the instance
72 22b7f6f8 Thomas Thrainer
  @type hypervisor_name: string
73 22b7f6f8 Thomas Thrainer
  @param hypervisor_name: the hypervisor for the instance
74 22b7f6f8 Thomas Thrainer
  @type tags: list
75 22b7f6f8 Thomas Thrainer
  @param tags: list of instance tags as strings
76 22b7f6f8 Thomas Thrainer
  @rtype: dict
77 22b7f6f8 Thomas Thrainer
  @return: the hook environment for this instance
78 22b7f6f8 Thomas Thrainer

79 22b7f6f8 Thomas Thrainer
  """
80 22b7f6f8 Thomas Thrainer
  env = {
81 22b7f6f8 Thomas Thrainer
    "OP_TARGET": name,
82 22b7f6f8 Thomas Thrainer
    "INSTANCE_NAME": name,
83 1c3231aa Thomas Thrainer
    "INSTANCE_PRIMARY": primary_node_name,
84 1c3231aa Thomas Thrainer
    "INSTANCE_SECONDARIES": " ".join(secondary_node_names),
85 22b7f6f8 Thomas Thrainer
    "INSTANCE_OS_TYPE": os_type,
86 22b7f6f8 Thomas Thrainer
    "INSTANCE_STATUS": status,
87 22b7f6f8 Thomas Thrainer
    "INSTANCE_MINMEM": minmem,
88 22b7f6f8 Thomas Thrainer
    "INSTANCE_MAXMEM": maxmem,
89 22b7f6f8 Thomas Thrainer
    # TODO(2.9) remove deprecated "memory" value
90 22b7f6f8 Thomas Thrainer
    "INSTANCE_MEMORY": maxmem,
91 22b7f6f8 Thomas Thrainer
    "INSTANCE_VCPUS": vcpus,
92 22b7f6f8 Thomas Thrainer
    "INSTANCE_DISK_TEMPLATE": disk_template,
93 22b7f6f8 Thomas Thrainer
    "INSTANCE_HYPERVISOR": hypervisor_name,
94 22b7f6f8 Thomas Thrainer
    }
95 22b7f6f8 Thomas Thrainer
  if nics:
96 22b7f6f8 Thomas Thrainer
    nic_count = len(nics)
97 d676dbea Sebastian Gebhard
    for idx, (name, uuid, ip, mac, mode, link, vlan, net, netinfo) \
98 d676dbea Sebastian Gebhard
        in enumerate(nics):
99 22b7f6f8 Thomas Thrainer
      if ip is None:
100 22b7f6f8 Thomas Thrainer
        ip = ""
101 8a348b15 Christos Stavrakakis
      if name:
102 8a348b15 Christos Stavrakakis
        env["INSTANCE_NIC%d_NAME" % idx] = name
103 8a348b15 Christos Stavrakakis
      env["INSTANCE_NIC%d_UUID" % idx] = uuid
104 22b7f6f8 Thomas Thrainer
      env["INSTANCE_NIC%d_IP" % idx] = ip
105 22b7f6f8 Thomas Thrainer
      env["INSTANCE_NIC%d_MAC" % idx] = mac
106 22b7f6f8 Thomas Thrainer
      env["INSTANCE_NIC%d_MODE" % idx] = mode
107 22b7f6f8 Thomas Thrainer
      env["INSTANCE_NIC%d_LINK" % idx] = link
108 d676dbea Sebastian Gebhard
      env["INSTANCE_NIC%d_VLAN" % idx] = vlan
109 22b7f6f8 Thomas Thrainer
      if netinfo:
110 22b7f6f8 Thomas Thrainer
        nobj = objects.Network.FromDict(netinfo)
111 22b7f6f8 Thomas Thrainer
        env.update(nobj.HooksDict("INSTANCE_NIC%d_" % idx))
112 22b7f6f8 Thomas Thrainer
      elif network:
113 22b7f6f8 Thomas Thrainer
        # FIXME: broken network reference: the instance NIC specifies a
114 22b7f6f8 Thomas Thrainer
        # network, but the relevant network entry was not in the config. This
115 22b7f6f8 Thomas Thrainer
        # should be made impossible.
116 22b7f6f8 Thomas Thrainer
        env["INSTANCE_NIC%d_NETWORK_NAME" % idx] = net
117 d676dbea Sebastian Gebhard
      if mode == constants.NIC_MODE_BRIDGED or \
118 d676dbea Sebastian Gebhard
         mode == constants.NIC_MODE_OVS:
119 22b7f6f8 Thomas Thrainer
        env["INSTANCE_NIC%d_BRIDGE" % idx] = link
120 22b7f6f8 Thomas Thrainer
  else:
121 22b7f6f8 Thomas Thrainer
    nic_count = 0
122 22b7f6f8 Thomas Thrainer
123 22b7f6f8 Thomas Thrainer
  env["INSTANCE_NIC_COUNT"] = nic_count
124 22b7f6f8 Thomas Thrainer
125 22b7f6f8 Thomas Thrainer
  if disks:
126 22b7f6f8 Thomas Thrainer
    disk_count = len(disks)
127 8a348b15 Christos Stavrakakis
    for idx, (name, uuid, size, mode) in enumerate(disks):
128 8a348b15 Christos Stavrakakis
      if name:
129 8a348b15 Christos Stavrakakis
        env["INSTANCE_DISK%d_NAME" % idx] = name
130 8a348b15 Christos Stavrakakis
      env["INSTANCE_DISK%d_UUID" % idx] = uuid
131 22b7f6f8 Thomas Thrainer
      env["INSTANCE_DISK%d_SIZE" % idx] = size
132 22b7f6f8 Thomas Thrainer
      env["INSTANCE_DISK%d_MODE" % idx] = mode
133 22b7f6f8 Thomas Thrainer
  else:
134 22b7f6f8 Thomas Thrainer
    disk_count = 0
135 22b7f6f8 Thomas Thrainer
136 22b7f6f8 Thomas Thrainer
  env["INSTANCE_DISK_COUNT"] = disk_count
137 22b7f6f8 Thomas Thrainer
138 22b7f6f8 Thomas Thrainer
  if not tags:
139 22b7f6f8 Thomas Thrainer
    tags = []
140 22b7f6f8 Thomas Thrainer
141 22b7f6f8 Thomas Thrainer
  env["INSTANCE_TAGS"] = " ".join(tags)
142 22b7f6f8 Thomas Thrainer
143 22b7f6f8 Thomas Thrainer
  for source, kind in [(bep, "BE"), (hvp, "HV")]:
144 22b7f6f8 Thomas Thrainer
    for key, value in source.items():
145 22b7f6f8 Thomas Thrainer
      env["INSTANCE_%s_%s" % (kind, key)] = value
146 22b7f6f8 Thomas Thrainer
147 22b7f6f8 Thomas Thrainer
  return env
148 22b7f6f8 Thomas Thrainer
149 22b7f6f8 Thomas Thrainer
150 e8dd6643 Ilias Tsitsimpis
def BuildInstanceHookEnvByObject(lu, instance, secondary_nodes=None,
151 e8dd6643 Ilias Tsitsimpis
                                 disks=None, override=None):
152 22b7f6f8 Thomas Thrainer
  """Builds instance related env variables for hooks from an object.
153 22b7f6f8 Thomas Thrainer

154 22b7f6f8 Thomas Thrainer
  @type lu: L{LogicalUnit}
155 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
156 22b7f6f8 Thomas Thrainer
  @type instance: L{objects.Instance}
157 22b7f6f8 Thomas Thrainer
  @param instance: the instance for which we should build the
158 22b7f6f8 Thomas Thrainer
      environment
159 22b7f6f8 Thomas Thrainer
  @type override: dict
160 22b7f6f8 Thomas Thrainer
  @param override: dictionary with key/values that will override
161 22b7f6f8 Thomas Thrainer
      our values
162 22b7f6f8 Thomas Thrainer
  @rtype: dict
163 22b7f6f8 Thomas Thrainer
  @return: the hook environment dictionary
164 22b7f6f8 Thomas Thrainer

165 22b7f6f8 Thomas Thrainer
  """
166 22b7f6f8 Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
167 22b7f6f8 Thomas Thrainer
  bep = cluster.FillBE(instance)
168 22b7f6f8 Thomas Thrainer
  hvp = cluster.FillHV(instance)
169 e8dd6643 Ilias Tsitsimpis
170 e8dd6643 Ilias Tsitsimpis
  # Override secondary_nodes
171 e8dd6643 Ilias Tsitsimpis
  if secondary_nodes is None:
172 45c044f4 Ilias Tsitsimpis
    secondary_nodes = lu.cfg.GetInstanceSecondaryNodes(instance.uuid)
173 e8dd6643 Ilias Tsitsimpis
174 e8dd6643 Ilias Tsitsimpis
  # Override disks
175 e8dd6643 Ilias Tsitsimpis
  if disks is None:
176 43b1f49f Ilias Tsitsimpis
    disks = lu.cfg.GetInstanceDisks(instance.uuid)
177 e8dd6643 Ilias Tsitsimpis
178 22b7f6f8 Thomas Thrainer
  args = {
179 22b7f6f8 Thomas Thrainer
    "name": instance.name,
180 1c3231aa Thomas Thrainer
    "primary_node_name": lu.cfg.GetNodeName(instance.primary_node),
181 e8dd6643 Ilias Tsitsimpis
    "secondary_node_names": lu.cfg.GetNodeNames(secondary_nodes),
182 22b7f6f8 Thomas Thrainer
    "os_type": instance.os,
183 22b7f6f8 Thomas Thrainer
    "status": instance.admin_state,
184 22b7f6f8 Thomas Thrainer
    "maxmem": bep[constants.BE_MAXMEM],
185 22b7f6f8 Thomas Thrainer
    "minmem": bep[constants.BE_MINMEM],
186 22b7f6f8 Thomas Thrainer
    "vcpus": bep[constants.BE_VCPUS],
187 5eacbcae Thomas Thrainer
    "nics": NICListToTuple(lu, instance.nics),
188 22b7f6f8 Thomas Thrainer
    "disk_template": instance.disk_template,
189 8a348b15 Christos Stavrakakis
    "disks": [(disk.name, disk.uuid, disk.size, disk.mode)
190 e8dd6643 Ilias Tsitsimpis
              for disk in disks],
191 22b7f6f8 Thomas Thrainer
    "bep": bep,
192 22b7f6f8 Thomas Thrainer
    "hvp": hvp,
193 22b7f6f8 Thomas Thrainer
    "hypervisor_name": instance.hypervisor,
194 22b7f6f8 Thomas Thrainer
    "tags": instance.tags,
195 22b7f6f8 Thomas Thrainer
  }
196 22b7f6f8 Thomas Thrainer
  if override:
197 22b7f6f8 Thomas Thrainer
    args.update(override)
198 5eacbcae Thomas Thrainer
  return BuildInstanceHookEnv(**args) # pylint: disable=W0142
199 22b7f6f8 Thomas Thrainer
200 22b7f6f8 Thomas Thrainer
201 5eacbcae Thomas Thrainer
def GetClusterDomainSecret():
202 22b7f6f8 Thomas Thrainer
  """Reads the cluster domain secret.
203 22b7f6f8 Thomas Thrainer

204 22b7f6f8 Thomas Thrainer
  """
205 22b7f6f8 Thomas Thrainer
  return utils.ReadOneLineFile(pathutils.CLUSTER_DOMAIN_SECRET_FILE,
206 22b7f6f8 Thomas Thrainer
                               strict=True)
207 22b7f6f8 Thomas Thrainer
208 22b7f6f8 Thomas Thrainer
209 1c3231aa Thomas Thrainer
def CheckNodeNotDrained(lu, node_uuid):
210 22b7f6f8 Thomas Thrainer
  """Ensure that a given node is not drained.
211 22b7f6f8 Thomas Thrainer

212 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
213 1c3231aa Thomas Thrainer
  @param node_uuid: the node to check
214 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is drained
215 22b7f6f8 Thomas Thrainer

216 22b7f6f8 Thomas Thrainer
  """
217 c214896c Thomas Thrainer
  node = lu.cfg.GetNodeInfo(node_uuid)
218 c214896c Thomas Thrainer
  if node.drained:
219 c214896c Thomas Thrainer
    raise errors.OpPrereqError("Can't use drained node %s" % node.name,
220 22b7f6f8 Thomas Thrainer
                               errors.ECODE_STATE)
221 22b7f6f8 Thomas Thrainer
222 22b7f6f8 Thomas Thrainer
223 1c3231aa Thomas Thrainer
def CheckNodeVmCapable(lu, node_uuid):
224 763ad5be Thomas Thrainer
  """Ensure that a given node is vm capable.
225 22b7f6f8 Thomas Thrainer

226 763ad5be Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
227 1c3231aa Thomas Thrainer
  @param node_uuid: the node to check
228 763ad5be Thomas Thrainer
  @raise errors.OpPrereqError: if the node is not vm capable
229 22b7f6f8 Thomas Thrainer

230 22b7f6f8 Thomas Thrainer
  """
231 1c3231aa Thomas Thrainer
  if not lu.cfg.GetNodeInfo(node_uuid).vm_capable:
232 1c3231aa Thomas Thrainer
    raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node_uuid,
233 763ad5be Thomas Thrainer
                               errors.ECODE_STATE)
234 22b7f6f8 Thomas Thrainer
235 22b7f6f8 Thomas Thrainer
236 5eacbcae Thomas Thrainer
def RemoveInstance(lu, feedback_fn, instance, ignore_failures):
237 22b7f6f8 Thomas Thrainer
  """Utility function to remove an instance.
238 22b7f6f8 Thomas Thrainer

239 22b7f6f8 Thomas Thrainer
  """
240 22b7f6f8 Thomas Thrainer
  logging.info("Removing block devices for instance %s", instance.name)
241 22b7f6f8 Thomas Thrainer
242 5eacbcae Thomas Thrainer
  if not RemoveDisks(lu, instance, ignore_failures=ignore_failures):
243 22b7f6f8 Thomas Thrainer
    if not ignore_failures:
244 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Can't remove instance's disks")
245 22b7f6f8 Thomas Thrainer
    feedback_fn("Warning: can't remove instance's disks")
246 22b7f6f8 Thomas Thrainer
247 43b1f49f Ilias Tsitsimpis
  logging.info("Removing instance's disks")
248 43b1f49f Ilias Tsitsimpis
  for disk in instance.disks:
249 43b1f49f Ilias Tsitsimpis
    lu.cfg.RemoveInstanceDisk(instance.uuid, disk)
250 22b7f6f8 Thomas Thrainer
251 43b1f49f Ilias Tsitsimpis
  logging.info("Removing instance %s out of cluster config", instance.name)
252 da4a52a3 Thomas Thrainer
  lu.cfg.RemoveInstance(instance.uuid)
253 22b7f6f8 Thomas Thrainer
254 22b7f6f8 Thomas Thrainer
255 1c3231aa Thomas Thrainer
def RemoveDisks(lu, instance, target_node_uuid=None, ignore_failures=False):
256 22b7f6f8 Thomas Thrainer
  """Remove all disks for an instance.
257 22b7f6f8 Thomas Thrainer

258 22b7f6f8 Thomas Thrainer
  This abstracts away some work from `AddInstance()` and
259 22b7f6f8 Thomas Thrainer
  `RemoveInstance()`. Note that in case some of the devices couldn't
260 22b7f6f8 Thomas Thrainer
  be removed, the removal will continue with the other ones.
261 22b7f6f8 Thomas Thrainer

262 22b7f6f8 Thomas Thrainer
  @type lu: L{LogicalUnit}
263 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
264 22b7f6f8 Thomas Thrainer
  @type instance: L{objects.Instance}
265 22b7f6f8 Thomas Thrainer
  @param instance: the instance whose disks we should remove
266 1c3231aa Thomas Thrainer
  @type target_node_uuid: string
267 1c3231aa Thomas Thrainer
  @param target_node_uuid: used to override the node on which to remove the
268 1c3231aa Thomas Thrainer
          disks
269 22b7f6f8 Thomas Thrainer
  @rtype: boolean
270 22b7f6f8 Thomas Thrainer
  @return: the success of the removal
271 22b7f6f8 Thomas Thrainer

272 22b7f6f8 Thomas Thrainer
  """
273 22b7f6f8 Thomas Thrainer
  logging.info("Removing block devices for instance %s", instance.name)
274 22b7f6f8 Thomas Thrainer
275 22b7f6f8 Thomas Thrainer
  all_result = True
276 22b7f6f8 Thomas Thrainer
  ports_to_release = set()
277 43b1f49f Ilias Tsitsimpis
  inst_disks = lu.cfg.GetInstanceDisks(instance.uuid)
278 43b1f49f Ilias Tsitsimpis
  anno_disks = AnnotateDiskParams(instance, inst_disks, lu.cfg)
279 22b7f6f8 Thomas Thrainer
  for (idx, device) in enumerate(anno_disks):
280 1c3231aa Thomas Thrainer
    if target_node_uuid:
281 1c3231aa Thomas Thrainer
      edata = [(target_node_uuid, device)]
282 22b7f6f8 Thomas Thrainer
    else:
283 22b7f6f8 Thomas Thrainer
      edata = device.ComputeNodeTree(instance.primary_node)
284 1c3231aa Thomas Thrainer
    for node_uuid, disk in edata:
285 0c3d9c7c Thomas Thrainer
      result = lu.rpc.call_blockdev_remove(node_uuid, (disk, instance))
286 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
287 22b7f6f8 Thomas Thrainer
        lu.LogWarning("Could not remove disk %s on node %s,"
288 1c3231aa Thomas Thrainer
                      " continuing anyway: %s", idx,
289 1c3231aa Thomas Thrainer
                      lu.cfg.GetNodeName(node_uuid), result.fail_msg)
290 1c3231aa Thomas Thrainer
        if not (result.offline and node_uuid != instance.primary_node):
291 22b7f6f8 Thomas Thrainer
          all_result = False
292 22b7f6f8 Thomas Thrainer
293 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
294 66a37e7a Helga Velroyen
    if device.dev_type in constants.DTS_DRBD:
295 22b7f6f8 Thomas Thrainer
      ports_to_release.add(device.logical_id[2])
296 22b7f6f8 Thomas Thrainer
297 22b7f6f8 Thomas Thrainer
  if all_result or ignore_failures:
298 22b7f6f8 Thomas Thrainer
    for port in ports_to_release:
299 22b7f6f8 Thomas Thrainer
      lu.cfg.AddTcpUdpPort(port)
300 22b7f6f8 Thomas Thrainer
301 1f7c8208 Helga Velroyen
  CheckDiskTemplateEnabled(lu.cfg.GetClusterInfo(), instance.disk_template)
302 1f7c8208 Helga Velroyen
303 845b7ed1 Santi Raffa
  if instance.disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
304 43b1f49f Ilias Tsitsimpis
    if len(inst_disks) > 0:
305 43b1f49f Ilias Tsitsimpis
      file_storage_dir = os.path.dirname(inst_disks[0].logical_id[1])
306 7e219d1b Klaus Aehlig
    else:
307 7e219d1b Klaus Aehlig
      if instance.disk_template == constants.DT_SHARED_FILE:
308 7e219d1b Klaus Aehlig
        file_storage_dir = utils.PathJoin(lu.cfg.GetSharedFileStorageDir(),
309 7e219d1b Klaus Aehlig
                                          instance.name)
310 7e219d1b Klaus Aehlig
      else:
311 7e219d1b Klaus Aehlig
        file_storage_dir = utils.PathJoin(lu.cfg.GetFileStorageDir(),
312 7e219d1b Klaus Aehlig
                                          instance.name)
313 1c3231aa Thomas Thrainer
    if target_node_uuid:
314 1c3231aa Thomas Thrainer
      tgt = target_node_uuid
315 22b7f6f8 Thomas Thrainer
    else:
316 22b7f6f8 Thomas Thrainer
      tgt = instance.primary_node
317 22b7f6f8 Thomas Thrainer
    result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
318 22b7f6f8 Thomas Thrainer
    if result.fail_msg:
319 22b7f6f8 Thomas Thrainer
      lu.LogWarning("Could not remove directory '%s' on node %s: %s",
320 1c3231aa Thomas Thrainer
                    file_storage_dir, lu.cfg.GetNodeName(tgt), result.fail_msg)
321 22b7f6f8 Thomas Thrainer
      all_result = False
322 22b7f6f8 Thomas Thrainer
323 22b7f6f8 Thomas Thrainer
  return all_result
324 22b7f6f8 Thomas Thrainer
325 22b7f6f8 Thomas Thrainer
326 5eacbcae Thomas Thrainer
def NICToTuple(lu, nic):
327 22b7f6f8 Thomas Thrainer
  """Build a tupple of nic information.
328 22b7f6f8 Thomas Thrainer

329 22b7f6f8 Thomas Thrainer
  @type lu:  L{LogicalUnit}
330 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
331 22b7f6f8 Thomas Thrainer
  @type nic: L{objects.NIC}
332 22b7f6f8 Thomas Thrainer
  @param nic: nic to convert to hooks tuple
333 22b7f6f8 Thomas Thrainer

334 22b7f6f8 Thomas Thrainer
  """
335 22b7f6f8 Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
336 22b7f6f8 Thomas Thrainer
  filled_params = cluster.SimpleFillNIC(nic.nicparams)
337 22b7f6f8 Thomas Thrainer
  mode = filled_params[constants.NIC_MODE]
338 22b7f6f8 Thomas Thrainer
  link = filled_params[constants.NIC_LINK]
339 d676dbea Sebastian Gebhard
  vlan = filled_params[constants.NIC_VLAN]
340 22b7f6f8 Thomas Thrainer
  netinfo = None
341 22b7f6f8 Thomas Thrainer
  if nic.network:
342 22b7f6f8 Thomas Thrainer
    nobj = lu.cfg.GetNetwork(nic.network)
343 22b7f6f8 Thomas Thrainer
    netinfo = objects.Network.ToDict(nobj)
344 d676dbea Sebastian Gebhard
  return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, vlan,
345 d676dbea Sebastian Gebhard
          nic.network, netinfo)
346 22b7f6f8 Thomas Thrainer
347 22b7f6f8 Thomas Thrainer
348 5eacbcae Thomas Thrainer
def NICListToTuple(lu, nics):
349 22b7f6f8 Thomas Thrainer
  """Build a list of nic information tuples.
350 22b7f6f8 Thomas Thrainer

351 22b7f6f8 Thomas Thrainer
  This list is suitable to be passed to _BuildInstanceHookEnv or as a return
352 22b7f6f8 Thomas Thrainer
  value in LUInstanceQueryData.
353 22b7f6f8 Thomas Thrainer

354 22b7f6f8 Thomas Thrainer
  @type lu:  L{LogicalUnit}
355 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
356 22b7f6f8 Thomas Thrainer
  @type nics: list of L{objects.NIC}
357 22b7f6f8 Thomas Thrainer
  @param nics: list of nics to convert to hooks tuples
358 22b7f6f8 Thomas Thrainer

359 22b7f6f8 Thomas Thrainer
  """
360 22b7f6f8 Thomas Thrainer
  hooks_nics = []
361 22b7f6f8 Thomas Thrainer
  for nic in nics:
362 5eacbcae Thomas Thrainer
    hooks_nics.append(NICToTuple(lu, nic))
363 22b7f6f8 Thomas Thrainer
  return hooks_nics
364 763ad5be Thomas Thrainer
365 763ad5be Thomas Thrainer
366 5eacbcae Thomas Thrainer
def CopyLockList(names):
367 763ad5be Thomas Thrainer
  """Makes a copy of a list of lock names.
368 763ad5be Thomas Thrainer

369 763ad5be Thomas Thrainer
  Handles L{locking.ALL_SET} correctly.
370 763ad5be Thomas Thrainer

371 763ad5be Thomas Thrainer
  """
372 763ad5be Thomas Thrainer
  if names == locking.ALL_SET:
373 763ad5be Thomas Thrainer
    return locking.ALL_SET
374 763ad5be Thomas Thrainer
  else:
375 763ad5be Thomas Thrainer
    return names[:]
376 763ad5be Thomas Thrainer
377 763ad5be Thomas Thrainer
378 5eacbcae Thomas Thrainer
def ReleaseLocks(lu, level, names=None, keep=None):
379 763ad5be Thomas Thrainer
  """Releases locks owned by an LU.
380 763ad5be Thomas Thrainer

381 763ad5be Thomas Thrainer
  @type lu: L{LogicalUnit}
382 763ad5be Thomas Thrainer
  @param level: Lock level
383 763ad5be Thomas Thrainer
  @type names: list or None
384 763ad5be Thomas Thrainer
  @param names: Names of locks to release
385 763ad5be Thomas Thrainer
  @type keep: list or None
386 763ad5be Thomas Thrainer
  @param keep: Names of locks to retain
387 763ad5be Thomas Thrainer

388 763ad5be Thomas Thrainer
  """
389 87ed6b79 Klaus Aehlig
  logging.debug("Lu %s ReleaseLocks %s names=%s, keep=%s",
390 87ed6b79 Klaus Aehlig
                lu.wconfdcontext, level, names, keep)
391 763ad5be Thomas Thrainer
  assert not (keep is not None and names is not None), \
392 763ad5be Thomas Thrainer
         "Only one of the 'names' and the 'keep' parameters can be given"
393 763ad5be Thomas Thrainer
394 763ad5be Thomas Thrainer
  if names is not None:
395 763ad5be Thomas Thrainer
    should_release = names.__contains__
396 763ad5be Thomas Thrainer
  elif keep:
397 763ad5be Thomas Thrainer
    should_release = lambda name: name not in keep
398 763ad5be Thomas Thrainer
  else:
399 763ad5be Thomas Thrainer
    should_release = None
400 763ad5be Thomas Thrainer
401 87ed6b79 Klaus Aehlig
  levelname = locking.LEVEL_NAMES[level]
402 87ed6b79 Klaus Aehlig
403 763ad5be Thomas Thrainer
  owned = lu.owned_locks(level)
404 763ad5be Thomas Thrainer
  if not owned:
405 763ad5be Thomas Thrainer
    # Not owning any lock at this level, do nothing
406 763ad5be Thomas Thrainer
    pass
407 763ad5be Thomas Thrainer
408 763ad5be Thomas Thrainer
  elif should_release:
409 763ad5be Thomas Thrainer
    retain = []
410 763ad5be Thomas Thrainer
    release = []
411 763ad5be Thomas Thrainer
412 763ad5be Thomas Thrainer
    # Determine which locks to release
413 763ad5be Thomas Thrainer
    for name in owned:
414 763ad5be Thomas Thrainer
      if should_release(name):
415 763ad5be Thomas Thrainer
        release.append(name)
416 763ad5be Thomas Thrainer
      else:
417 763ad5be Thomas Thrainer
        retain.append(name)
418 763ad5be Thomas Thrainer
419 763ad5be Thomas Thrainer
    assert len(lu.owned_locks(level)) == (len(retain) + len(release))
420 763ad5be Thomas Thrainer
421 763ad5be Thomas Thrainer
    # Release just some locks
422 87ed6b79 Klaus Aehlig
    lu.WConfdClient().TryUpdateLocks(
423 87ed6b79 Klaus Aehlig
      lu.release_request(level, release))
424 763ad5be Thomas Thrainer
    assert frozenset(lu.owned_locks(level)) == frozenset(retain)
425 763ad5be Thomas Thrainer
  else:
426 87ed6b79 Klaus Aehlig
    lu.WConfdClient().FreeLocksLevel(levelname)
427 763ad5be Thomas Thrainer
428 763ad5be Thomas Thrainer
429 763ad5be Thomas Thrainer
def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group,
430 763ad5be Thomas Thrainer
                                 target_group, cfg,
431 5eacbcae Thomas Thrainer
                                 _compute_fn=ComputeIPolicyInstanceViolation):
432 763ad5be Thomas Thrainer
  """Compute if instance meets the specs of the new target group.
433 763ad5be Thomas Thrainer

434 763ad5be Thomas Thrainer
  @param ipolicy: The ipolicy to verify
435 763ad5be Thomas Thrainer
  @param instance: The instance object to verify
436 763ad5be Thomas Thrainer
  @param current_group: The current group of the instance
437 763ad5be Thomas Thrainer
  @param target_group: The new group of the instance
438 763ad5be Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
439 763ad5be Thomas Thrainer
  @param cfg: Cluster configuration
440 763ad5be Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
441 5eacbcae Thomas Thrainer
  @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
442 763ad5be Thomas Thrainer

443 763ad5be Thomas Thrainer
  """
444 763ad5be Thomas Thrainer
  if current_group == target_group:
445 763ad5be Thomas Thrainer
    return []
446 763ad5be Thomas Thrainer
  else:
447 763ad5be Thomas Thrainer
    return _compute_fn(ipolicy, instance, cfg)
448 763ad5be Thomas Thrainer
449 763ad5be Thomas Thrainer
450 5eacbcae Thomas Thrainer
def CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False,
451 5eacbcae Thomas Thrainer
                           _compute_fn=_ComputeIPolicyNodeViolation):
452 763ad5be Thomas Thrainer
  """Checks that the target node is correct in terms of instance policy.
453 763ad5be Thomas Thrainer

454 763ad5be Thomas Thrainer
  @param ipolicy: The ipolicy to verify
455 763ad5be Thomas Thrainer
  @param instance: The instance object to verify
456 763ad5be Thomas Thrainer
  @param node: The new node to relocate
457 763ad5be Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
458 763ad5be Thomas Thrainer
  @param cfg: Cluster configuration
459 763ad5be Thomas Thrainer
  @param ignore: Ignore violations of the ipolicy
460 763ad5be Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
461 5eacbcae Thomas Thrainer
  @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
462 763ad5be Thomas Thrainer

463 763ad5be Thomas Thrainer
  """
464 763ad5be Thomas Thrainer
  primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
465 763ad5be Thomas Thrainer
  res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
466 763ad5be Thomas Thrainer
467 763ad5be Thomas Thrainer
  if res:
468 763ad5be Thomas Thrainer
    msg = ("Instance does not meet target node group's (%s) instance"
469 763ad5be Thomas Thrainer
           " policy: %s") % (node.group, utils.CommaJoin(res))
470 763ad5be Thomas Thrainer
    if ignore:
471 763ad5be Thomas Thrainer
      lu.LogWarning(msg)
472 763ad5be Thomas Thrainer
    else:
473 763ad5be Thomas Thrainer
      raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
474 763ad5be Thomas Thrainer
475 763ad5be Thomas Thrainer
476 5eacbcae Thomas Thrainer
def GetInstanceInfoText(instance):
477 763ad5be Thomas Thrainer
  """Compute that text that should be added to the disk's metadata.
478 763ad5be Thomas Thrainer

479 763ad5be Thomas Thrainer
  """
480 763ad5be Thomas Thrainer
  return "originstname+%s" % instance.name
481 87e25be1 Thomas Thrainer
482 87e25be1 Thomas Thrainer
483 1c3231aa Thomas Thrainer
def CheckNodeFreeMemory(lu, node_uuid, reason, requested, hvname, hvparams):
484 87e25be1 Thomas Thrainer
  """Checks if a node has enough free memory.
485 87e25be1 Thomas Thrainer

486 87e25be1 Thomas Thrainer
  This function checks if a given node has the needed amount of free
487 87e25be1 Thomas Thrainer
  memory. In case the node has less memory or we cannot get the
488 87e25be1 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
489 87e25be1 Thomas Thrainer
  exception.
490 87e25be1 Thomas Thrainer

491 87e25be1 Thomas Thrainer
  @type lu: C{LogicalUnit}
492 87e25be1 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
493 1c3231aa Thomas Thrainer
  @type node_uuid: C{str}
494 1c3231aa Thomas Thrainer
  @param node_uuid: the node to check
495 87e25be1 Thomas Thrainer
  @type reason: C{str}
496 87e25be1 Thomas Thrainer
  @param reason: string to use in the error message
497 87e25be1 Thomas Thrainer
  @type requested: C{int}
498 87e25be1 Thomas Thrainer
  @param requested: the amount of memory in MiB to check for
499 a295eb80 Helga Velroyen
  @type hvname: string
500 a295eb80 Helga Velroyen
  @param hvname: the hypervisor's name
501 a295eb80 Helga Velroyen
  @type hvparams: dict of strings
502 a295eb80 Helga Velroyen
  @param hvparams: the hypervisor's parameters
503 87e25be1 Thomas Thrainer
  @rtype: integer
504 87e25be1 Thomas Thrainer
  @return: node current free memory
505 87e25be1 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough memory, or
506 87e25be1 Thomas Thrainer
      we cannot check the node
507 87e25be1 Thomas Thrainer

508 87e25be1 Thomas Thrainer
  """
509 1c3231aa Thomas Thrainer
  node_name = lu.cfg.GetNodeName(node_uuid)
510 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info([node_uuid], None, [(hvname, hvparams)])
511 1c3231aa Thomas Thrainer
  nodeinfo[node_uuid].Raise("Can't get data from node %s" % node_name,
512 1c3231aa Thomas Thrainer
                            prereq=True, ecode=errors.ECODE_ENVIRON)
513 1c3231aa Thomas Thrainer
  (_, _, (hv_info, )) = nodeinfo[node_uuid].payload
514 87e25be1 Thomas Thrainer
515 87e25be1 Thomas Thrainer
  free_mem = hv_info.get("memory_free", None)
516 87e25be1 Thomas Thrainer
  if not isinstance(free_mem, int):
517 87e25be1 Thomas Thrainer
    raise errors.OpPrereqError("Can't compute free memory on node %s, result"
518 1c3231aa Thomas Thrainer
                               " was '%s'" % (node_name, free_mem),
519 87e25be1 Thomas Thrainer
                               errors.ECODE_ENVIRON)
520 87e25be1 Thomas Thrainer
  if requested > free_mem:
521 87e25be1 Thomas Thrainer
    raise errors.OpPrereqError("Not enough memory on node %s for %s:"
522 87e25be1 Thomas Thrainer
                               " needed %s MiB, available %s MiB" %
523 1c3231aa Thomas Thrainer
                               (node_name, reason, requested, free_mem),
524 87e25be1 Thomas Thrainer
                               errors.ECODE_NORES)
525 87e25be1 Thomas Thrainer
  return free_mem
526 87e25be1 Thomas Thrainer
527 87e25be1 Thomas Thrainer
528 1c3231aa Thomas Thrainer
def CheckInstanceBridgesExist(lu, instance, node_uuid=None):
529 87e25be1 Thomas Thrainer
  """Check that the brigdes needed by an instance exist.
530 87e25be1 Thomas Thrainer

531 87e25be1 Thomas Thrainer
  """
532 1c3231aa Thomas Thrainer
  if node_uuid is None:
533 1c3231aa Thomas Thrainer
    node_uuid = instance.primary_node
534 1c3231aa Thomas Thrainer
  CheckNicsBridgesExist(lu, instance.nics, node_uuid)
535 87e25be1 Thomas Thrainer
536 87e25be1 Thomas Thrainer
537 1c3231aa Thomas Thrainer
def CheckNicsBridgesExist(lu, nics, node_uuid):
538 87e25be1 Thomas Thrainer
  """Check that the brigdes needed by a list of nics exist.
539 87e25be1 Thomas Thrainer

540 87e25be1 Thomas Thrainer
  """
541 87e25be1 Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
542 1c3231aa Thomas Thrainer
  paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in nics]
543 87e25be1 Thomas Thrainer
  brlist = [params[constants.NIC_LINK] for params in paramslist
544 87e25be1 Thomas Thrainer
            if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
545 87e25be1 Thomas Thrainer
  if brlist:
546 1c3231aa Thomas Thrainer
    result = lu.rpc.call_bridges_exist(node_uuid, brlist)
547 87e25be1 Thomas Thrainer
    result.Raise("Error checking bridges on destination node '%s'" %
548 1c3231aa Thomas Thrainer
                 lu.cfg.GetNodeName(node_uuid), prereq=True,
549 1c3231aa Thomas Thrainer
                 ecode=errors.ECODE_ENVIRON)
550 0ef72034 Jose A. Lopes
551 0ef72034 Jose A. Lopes
552 0ef72034 Jose A. Lopes
def UpdateMetadata(feedback_fn, rpc, instance,
553 0ef72034 Jose A. Lopes
                   osparams_public=None,
554 0ef72034 Jose A. Lopes
                   osparams_private=None,
555 0ef72034 Jose A. Lopes
                   osparams_secret=None):
556 0ef72034 Jose A. Lopes
  """Updates instance metadata on the metadata daemon on the
557 0ef72034 Jose A. Lopes
  instance's primary node.
558 0ef72034 Jose A. Lopes

559 0ef72034 Jose A. Lopes
  In case the RPC fails, this function simply issues a warning and
560 0ef72034 Jose A. Lopes
  proceeds normally.
561 0ef72034 Jose A. Lopes

562 0ef72034 Jose A. Lopes
  @type feedback_fn: callable
563 0ef72034 Jose A. Lopes
  @param feedback_fn: function used send feedback back to the caller
564 0ef72034 Jose A. Lopes

565 0ef72034 Jose A. Lopes
  @type rpc: L{rpc.node.RpcRunner}
566 0ef72034 Jose A. Lopes
  @param rpc: RPC runner
567 0ef72034 Jose A. Lopes

568 0ef72034 Jose A. Lopes
  @type instance: L{objects.Instance}
569 0ef72034 Jose A. Lopes
  @param instance: instance for which the metadata should be updated
570 0ef72034 Jose A. Lopes

571 0ef72034 Jose A. Lopes
  @type osparams_public: NoneType or dict
572 0ef72034 Jose A. Lopes
  @param osparams_public: public OS parameters used to override those
573 0ef72034 Jose A. Lopes
                          defined in L{instance}
574 0ef72034 Jose A. Lopes

575 0ef72034 Jose A. Lopes
  @type osparams_private: NoneType or dict
576 0ef72034 Jose A. Lopes
  @param osparams_private: private OS parameters used to override those
577 0ef72034 Jose A. Lopes
                           defined in L{instance}
578 0ef72034 Jose A. Lopes

579 0ef72034 Jose A. Lopes
  @type osparams_secret: NoneType or dict
580 0ef72034 Jose A. Lopes
  @param osparams_secret: secret OS parameters used to override those
581 0ef72034 Jose A. Lopes
                          defined in L{instance}
582 0ef72034 Jose A. Lopes

583 0ef72034 Jose A. Lopes
  @rtype: NoneType
584 0ef72034 Jose A. Lopes
  @return: None
585 0ef72034 Jose A. Lopes

586 0ef72034 Jose A. Lopes
  """
587 0ef72034 Jose A. Lopes
  data = instance.ToDict()
588 0ef72034 Jose A. Lopes
589 0ef72034 Jose A. Lopes
  if osparams_public is not None:
590 0ef72034 Jose A. Lopes
    data["osparams_public"] = osparams_public
591 0ef72034 Jose A. Lopes
592 0ef72034 Jose A. Lopes
  if osparams_private is not None:
593 0ef72034 Jose A. Lopes
    data["osparams_private"] = osparams_private
594 0ef72034 Jose A. Lopes
595 0ef72034 Jose A. Lopes
  if osparams_secret is not None:
596 0ef72034 Jose A. Lopes
    data["osparams_secret"] = osparams_secret
597 0ef72034 Jose A. Lopes
  else:
598 0ef72034 Jose A. Lopes
    data["osparams_secret"] = {}
599 0ef72034 Jose A. Lopes
600 0ef72034 Jose A. Lopes
  result = rpc.call_instance_metadata_modify(instance.primary_node, data)
601 0ef72034 Jose A. Lopes
  result.Warn("Could not update metadata for instance '%s'" % instance.name,
602 0ef72034 Jose A. Lopes
              feedback_fn)
603 047f59ce Hrvoje Ribicic
604 047f59ce Hrvoje Ribicic
605 047f59ce Hrvoje Ribicic
def CheckCompressionTool(lu, compression_tool):
606 047f59ce Hrvoje Ribicic
  """ Checks if the provided compression tool is allowed to be used.
607 047f59ce Hrvoje Ribicic

608 047f59ce Hrvoje Ribicic
  @type compression_tool: string
609 047f59ce Hrvoje Ribicic
  @param compression_tool: Compression tool to use for importing or exporting
610 047f59ce Hrvoje Ribicic
    the instance
611 047f59ce Hrvoje Ribicic

612 047f59ce Hrvoje Ribicic
  @rtype: NoneType
613 047f59ce Hrvoje Ribicic
  @return: None
614 047f59ce Hrvoje Ribicic

615 047f59ce Hrvoje Ribicic
  @raise errors.OpPrereqError: If the tool is not enabled by Ganeti or
616 047f59ce Hrvoje Ribicic
                               whitelisted
617 047f59ce Hrvoje Ribicic

618 047f59ce Hrvoje Ribicic
  """
619 047f59ce Hrvoje Ribicic
  allowed_tools = lu.cfg.GetCompressionTools()
620 047f59ce Hrvoje Ribicic
  if (compression_tool != constants.IEC_NONE and
621 047f59ce Hrvoje Ribicic
      compression_tool not in allowed_tools):
622 047f59ce Hrvoje Ribicic
    raise errors.OpPrereqError(
623 047f59ce Hrvoje Ribicic
      "Compression tool not allowed, tools allowed are [%s]"
624 047f59ce Hrvoje Ribicic
      % ", ".join(allowed_tools)
625 047f59ce Hrvoje Ribicic
    )