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 """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
47
48
50 """Checks if a particular disk template is enabled.
51
52 """
53 return disk_template in enabled_disk_templates
54
55
61
62
68
69
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
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
116
117
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
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
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
185 logging.warning("Storage space information requested for"
186 " ambiguous storage type '%s'.", storage_type)
187 return result
188
189
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
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
239
240
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
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
262
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
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
283
284
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
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