Package ganeti :: Package utils :: Module storage
[hide private]
[frames] | no frames]

Source Code for Module ganeti.utils.storage

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2013 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30  """Utility functions for storage. 
 31   
 32  """ 
 33   
 34  import logging 
 35   
 36  from ganeti import constants 
 37  from ganeti import errors 
 38  from ganeti.utils import io as utils_io 
 39  from ganeti.utils import process as utils_process 
 40   
 41   
42 -def GetDiskTemplatesOfStorageTypes(*storage_types):
43 """Given the storage type, returns a list of disk templates based on that 44 storage type.""" 45 return [dt for dt in constants.DISK_TEMPLATES 46 if constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[dt] in storage_types]
47 48
49 -def IsDiskTemplateEnabled(disk_template, enabled_disk_templates):
50 """Checks if a particular disk template is enabled. 51 52 """ 53 return disk_template in enabled_disk_templates
54 55
56 -def IsFileStorageEnabled(enabled_disk_templates):
57 """Checks if file storage is enabled. 58 59 """ 60 return IsDiskTemplateEnabled(constants.DT_FILE, enabled_disk_templates)
61 62
63 -def IsSharedFileStorageEnabled(enabled_disk_templates):
64 """Checks if shared file storage is enabled. 65 66 """ 67 return IsDiskTemplateEnabled(constants.DT_SHARED_FILE, enabled_disk_templates)
68 69
70 -def IsLvmEnabled(enabled_disk_templates):
71 """Check whether or not any lvm-based disk templates are enabled.""" 72 return len(constants.DTS_LVM & set(enabled_disk_templates)) != 0
73 74
75 -def LvmGetsEnabled(enabled_disk_templates, new_enabled_disk_templates):
76 """Checks whether lvm was not enabled before, but will be enabled after 77 the operation. 78 79 """ 80 if IsLvmEnabled(enabled_disk_templates): 81 return False 82 return len(constants.DTS_LVM & set(new_enabled_disk_templates)) != 0
83 84
85 -def _GetDefaultStorageUnitForDiskTemplate(cfg, disk_template):
86 """Retrieves the identifier of the default storage entity for the given 87 storage type. 88 89 @type cfg: C{objects.ConfigData} 90 @param cfg: the configuration data 91 @type disk_template: string 92 @param disk_template: a disk template, for example 'drbd' 93 @rtype: string 94 @return: identifier for a storage unit, for example the vg_name for lvm 95 storage 96 97 """ 98 storage_type = constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template] 99 cluster = cfg.GetClusterInfo() 100 if disk_template in constants.DTS_LVM: 101 return (storage_type, cfg.GetVGName()) 102 elif disk_template == constants.DT_FILE: 103 return (storage_type, cluster.file_storage_dir) 104 elif disk_template == constants.DT_SHARED_FILE: 105 return (storage_type, cluster.shared_file_storage_dir) 106 elif disk_template == constants.DT_GLUSTER: 107 return (storage_type, cluster.gluster_storage_dir) 108 else: 109 return (storage_type, None)
110 111
112 -def DiskTemplateSupportsSpaceReporting(disk_template):
113 """Check whether the disk template supports storage space reporting.""" 114 return (constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template] 115 in constants.STS_REPORT)
116 117
118 -def GetStorageUnits(cfg, disk_templates):
119 """Get the cluster's storage units for the given disk templates. 120 121 If any lvm-based disk template is requested, spindle information 122 is added to the request. 123 124 @type cfg: L{config.ConfigWriter} 125 @param cfg: Cluster configuration 126 @type disk_templates: list of string 127 @param disk_templates: list of disk templates for which the storage 128 units will be computed 129 @rtype: list of tuples (string, string) 130 @return: list of storage units, each storage unit being a tuple of 131 (storage_type, storage_key); storage_type is in 132 C{constants.STORAGE_TYPES} and the storage_key a string to 133 identify an entity of that storage type, for example a volume group 134 name for LVM storage or a file for file storage. 135 136 """ 137 storage_units = [] 138 for disk_template in disk_templates: 139 if DiskTemplateSupportsSpaceReporting(disk_template): 140 storage_units.append( 141 _GetDefaultStorageUnitForDiskTemplate(cfg, disk_template)) 142 return storage_units
143 144
145 -def LookupSpaceInfoByDiskTemplate(storage_space_info, disk_template):
146 """Looks up the storage space info for a given disk template. 147 148 @type storage_space_info: list of dicts 149 @param storage_space_info: result of C{GetNodeInfo} 150 @type disk_template: string 151 @param disk_template: disk template to get storage space info 152 @rtype: tuple 153 @return: returns the element of storage_space_info that matches the given 154 disk template 155 156 """ 157 storage_type = constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[disk_template] 158 return LookupSpaceInfoByStorageType(storage_space_info, storage_type)
159 160
161 -def LookupSpaceInfoByStorageType(storage_space_info, storage_type):
162 """Looks up the storage space info for a given storage type. 163 164 Note that this lookup can be ambiguous if storage space reporting for several 165 units of the same storage type was requested. This function is only supposed 166 to be used for legacy code in situations where it actually is unambiguous. 167 168 @type storage_space_info: list of dicts 169 @param storage_space_info: result of C{GetNodeInfo} 170 @type storage_type: string 171 @param storage_type: a storage type, which is included in the storage_units 172 list 173 @rtype: tuple 174 @return: returns the element of storage_space_info that matches the given 175 storage type 176 177 """ 178 result = None 179 for unit_info in storage_space_info: 180 if unit_info["type"] == storage_type: 181 if result is None: 182 result = unit_info 183 else: 184 # There is more than one storage type in the query, log a warning 185 logging.warning("Storage space information requested for" 186 " ambiguous storage type '%s'.", storage_type) 187 return result
188 189
190 -def GetDiskLabels(prefix, num_disks, start=0):
191 """Generate disk labels for a number of disks 192 193 Note that disk labels are generated in the range [start..num_disks[ 194 (e.g., as in range(start, num_disks)) 195 196 @type prefix: string 197 @param prefix: disk label prefix (e.g., "/dev/sd") 198 199 @type num_disks: int 200 @param num_disks: number of disks (i.e., disk labels) 201 202 @type start: int 203 @param start: optional start index 204 205 @rtype: generator 206 @return: generator for the disk labels 207 208 """ 209 def _GetDiskSuffix(i): 210 n = ord('z') - ord('a') + 1 211 if i < n: 212 return chr(ord('a') + i) 213 else: 214 mod = int(i % n) 215 pref = _GetDiskSuffix((i - mod) / (n + 1)) 216 suf = _GetDiskSuffix(mod) 217 return pref + suf
218 219 for i in range(start, num_disks): 220 yield prefix + _GetDiskSuffix(i) 221 222
223 -def CreateBdevPartitionMapping(image_path):
224 """Create dm device for each partition of disk image. 225 226 This operation will allocate a loopback and a device-mapper device to map 227 partitions. 228 You must call L{ReleaseBdevPartitionMapping} to clean up resources allocated 229 by this function call. 230 231 @type image_path: string 232 @param image_path: path of multi-partition disk image 233 @rtype: tuple(string, list(string)) or NoneType 234 @return: returns the tuple(loopback_device, list(device_mapper_files)) if 235 image_path is a multi-partition disk image. otherwise, returns None. 236 237 """ 238 # Unfortunately, there are two different losetup commands in this world. 239 # One has the '-s' switch and the other has the '--show' switch to provide the 240 # same functionality. 241 result = utils_process.RunCmd(["losetup", "-f", "-s", image_path]) 242 if result.failed and "invalid option -- 's'" in result.stderr: 243 result = utils_process.RunCmd(["losetup", "-f", "--show", image_path]) 244 if result.failed: 245 raise errors.CommandError("Failed to setup loop device for %s: %s" % 246 (image_path, result.output)) 247 loop_dev_path = result.stdout.strip() 248 logging.debug("Loop dev %s allocated for %s", loop_dev_path, image_path) 249 250 result = utils_process.RunCmd(["kpartx", "-a", "-v", loop_dev_path]) 251 if result.failed: 252 # Just try to cleanup allocated loop device 253 utils_process.RunCmd(["losetup", "-d", loop_dev_path]) 254 raise errors.CommandError("Failed to add partition mapping for %s: %s" % 255 (image_path, result.output)) 256 dm_devs = [x.split(" ") for x in result.stdout.split("\n") if x] 257 if dm_devs: 258 dm_dev_paths = [utils_io.PathJoin("/dev/mapper", x[2]) for x in dm_devs] 259 return (loop_dev_path, dm_dev_paths) 260 else: 261 # image_path is not a multi partition disk image, no need to use 262 # device-mapper. 263 logging.debug("Release loop dev %s allocated for %s", 264 loop_dev_path, image_path) 265 ReleaseBdevPartitionMapping(loop_dev_path) 266 return None
267 268
269 -def ReleaseBdevPartitionMapping(loop_dev_path):
270 """Release allocated dm devices and loopback devices. 271 272 @type loop_dev_path: string 273 @param loop_dev_path: path of loopback device returned by 274 L{CreateBdevPartitionMapping} 275 276 """ 277 result = utils_process.RunCmd(["kpartx", "-d", loop_dev_path]) 278 if result.failed: 279 raise errors.CommandError("Failed to release partition mapping of %s: %s" % 280 (loop_dev_path, result.output)) 281 282 # The invocation of udevadm settle was added here because users had issues 283 # with the loopback device still being busy after kpartx / earlier commands 284 # did their work. 285 result = utils_process.RunCmd(["udevadm", "settle"]) 286 if result.failed: 287 raise errors.CommandError("Waiting on udev failed: %s" % result.output) 288 289 result = utils_process.RunCmd(["losetup", "-d", loop_dev_path]) 290 if result.failed: 291 raise errors.CommandError("Failed to detach %s: %s" % 292 (loop_dev_path, result.output))
293 294
295 -def osminor(dev):
296 """Return the device minor number from a raw device number. 297 298 This is a replacement for os.minor working around the issue that 299 Python's os.minor still has the old definition. See Ganeti issue 300 1058 for more details. 301 """ 302 return (dev & 0xff) | ((dev >> 12) & ~0xff)
303