1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Storage container abstraction.
23
24 """
25
26
27
28
29
30
31
32
33
34
35 import logging
36
37 from ganeti import errors
38 from ganeti import constants
39 from ganeti import utils
43 return int(round(float(value), 0))
44
47 """Base class for storage abstraction.
48
49 """
50 - def List(self, name, fields):
51 """Returns a list of all entities within the storage unit.
52
53 @type name: string or None
54 @param name: Entity name or None for all
55 @type fields: list
56 @param fields: List with all requested result fields (order is preserved)
57
58 """
59 raise NotImplementedError()
60
61 - def Modify(self, name, changes):
62 """Modifies an entity within the storage unit.
63
64 @type name: string
65 @param name: Entity name
66 @type changes: dict
67 @param changes: New field values
68
69 """
70
71 if changes:
72 raise errors.ProgrammerError("Unable to modify the following"
73 "fields: %r" % (changes.keys(), ))
74
76 """Executes an operation on an entity within the storage unit.
77
78 @type name: string
79 @param name: Entity name
80 @type op: string
81 @param op: Operation name
82
83 """
84 raise NotImplementedError()
85
88 """File storage unit.
89
90 """
92 """Initializes this class.
93
94 @type paths: list
95 @param paths: List of file storage paths
96
97 """
98 self._paths = paths
99
100 - def List(self, name, fields):
101 """Returns a list of all entities within the storage unit.
102
103 See L{_Base.List}.
104
105 """
106 rows = []
107
108 if name is None:
109 paths = self._paths
110 else:
111 paths = [name]
112
113 for path in paths:
114 rows.append(self._ListInner(path, fields))
115
116 return rows
117
118 @staticmethod
162
165 """Base class for LVM storage containers.
166
167 @cvar LIST_FIELDS: list of tuples consisting of three elements: SF_*
168 constants, lvm command output fields (list), and conversion
169 function or static value (for static value, the lvm output field
170 can be an empty list)
171
172 """
173 LIST_SEP = "|"
174 LIST_COMMAND = None
175 LIST_FIELDS = None
176
177 - def List(self, name, wanted_field_names):
199
200 @staticmethod
202 """Returns unique list of fields wanted from LVM command.
203
204 @type fields_def: list
205 @param fields_def: Field definitions
206 @type wanted_field_names: list
207 @param wanted_field_names: List of requested fields
208
209 """
210 field_to_idx = dict([(field_name, idx)
211 for (idx, (field_name, _, _)) in
212 enumerate(fields_def)])
213
214 lvm_fields = []
215
216 for field_name in wanted_field_names:
217 try:
218 idx = field_to_idx[field_name]
219 except IndexError:
220 raise errors.StorageError("Unknown field: %r" % field_name)
221
222 (_, lvm_names, _) = fields_def[idx]
223
224 lvm_fields.extend(lvm_names)
225
226 return utils.UniqueSequence(lvm_fields)
227
228 @classmethod
229 - def _BuildList(cls, cmd_result, fields_def, wanted_field_names, lvm_fields):
230 """Builds the final result list.
231
232 @type cmd_result: iterable
233 @param cmd_result: Iterable of LVM command output (iterable of lists)
234 @type fields_def: list
235 @param fields_def: Field definitions
236 @type wanted_field_names: list
237 @param wanted_field_names: List of requested fields
238 @type lvm_fields: list
239 @param lvm_fields: LVM fields
240
241 """
242 lvm_name_to_idx = dict([(lvm_name, idx)
243 for (idx, lvm_name) in enumerate(lvm_fields)])
244 field_to_idx = dict([(field_name, idx)
245 for (idx, (field_name, _, _)) in
246 enumerate(fields_def)])
247
248 data = []
249 for raw_data in cmd_result:
250 row = []
251
252 for field_name in wanted_field_names:
253 (_, lvm_names, mapper) = fields_def[field_to_idx[field_name]]
254
255 values = [raw_data[lvm_name_to_idx[i]] for i in lvm_names]
256
257 if callable(mapper):
258
259 val = mapper(*values)
260 elif len(values) == 1:
261
262
263 val = values[0]
264 else:
265
266
267 assert not values, "LVM storage has multi-fields without a function"
268 val = mapper
269
270 row.append(val)
271
272 data.append(row)
273
274 return data
275
276 @staticmethod
278 """Builds LVM command line.
279
280 @type cmd: string
281 @param cmd: Command name
282 @type sep: string
283 @param sep: Field separator character
284 @type options: list of strings
285 @param options: Wanted LVM fields
286 @type name: name or None
287 @param name: Name of requested entity
288
289 """
290 args = [cmd,
291 "--noheadings", "--units=m", "--nosuffix",
292 "--separator", sep,
293 "--options", ",".join(options)]
294
295 if name is not None:
296 args.append(name)
297
298 return args
299
300 @staticmethod
302 """Run LVM command.
303
304 """
305 result = utils.RunCmd(args)
306
307 if result.failed:
308 raise errors.StorageError("Failed to run %r, command output: %s" %
309 (args[0], result.output))
310
311 return result.stdout
312
313 @staticmethod
315 """Splits LVM command output into rows and fields.
316
317 @type data: string
318 @param data: LVM command output
319 @type sep: string
320 @param sep: Field separator character
321 @type fieldcount: int
322 @param fieldcount: Expected number of fields
323
324 """
325 for line in data.splitlines():
326 fields = line.strip().split(sep)
327
328 if len(fields) != fieldcount:
329 logging.warning("Invalid line returned from lvm command: %s", line)
330 continue
331
332 yield fields
333
336 """LVM Physical Volume storage unit.
337
338 """
339 @staticmethod
341 if attr:
342 return (attr[0] == "a")
343 else:
344 logging.warning("Invalid PV attribute: %r", attr)
345 return False
346
347 LIST_COMMAND = "pvs"
348
349
350
351 LIST_FIELDS = [
352 (constants.SF_NAME, ["pv_name"], None),
353 (constants.SF_SIZE, ["pv_size"], _ParseSize),
354 (constants.SF_USED, ["pv_used"], _ParseSize),
355 (constants.SF_FREE, ["pv_free"], _ParseSize),
356 (constants.SF_ALLOCATABLE, ["pv_attr"], _GetAllocatable),
357 ]
358
360 """Sets the "allocatable" flag on a physical volume.
361
362 @type name: string
363 @param name: Physical volume name
364 @type allocatable: bool
365 @param allocatable: Whether to set the "allocatable" flag
366
367 """
368 args = ["pvchange", "--allocatable"]
369
370 if allocatable:
371 args.append("y")
372 else:
373 args.append("n")
374
375 args.append(name)
376
377 result = utils.RunCmd(args)
378 if result.failed:
379 raise errors.StorageError("Failed to modify physical volume,"
380 " pvchange output: %s" %
381 result.output)
382
383 - def Modify(self, name, changes):
395
398 """LVM Volume Group storage unit.
399
400 """
401 LIST_COMMAND = "vgs"
402
403
404
405 LIST_FIELDS = [
406 (constants.SF_NAME, ["vg_name"], None),
407 (constants.SF_SIZE, ["vg_size"], _ParseSize),
408 (constants.SF_FREE, ["vg_free"], _ParseSize),
409 (constants.SF_USED, ["vg_size", "vg_free"],
410 lambda x, y: _ParseSize(x) - _ParseSize(y)),
411 (constants.SF_ALLOCATABLE, [], True),
412 ]
413
415 """Runs "vgreduce --removemissing" on a volume group.
416
417 @type name: string
418 @param name: Volume group name
419
420 """
421
422
423
424 result = utils.RunCmd(["vgreduce", "--removemissing", name])
425
426
427 vgreduce_output = result.output
428
429 result = utils.RunCmd(["vgs", "--noheadings", "--nosuffix", name])
430 if result.failed:
431 raise errors.StorageError(("Volume group '%s' still not consistent,"
432 " 'vgreduce' output: %r,"
433 " 'vgs' output: %r") %
434 (name, vgreduce_output, result.output))
435
446
447
448
449 _STORAGE_TYPES = {
450 constants.ST_FILE: FileStorage,
451 constants.ST_LVM_PV: LvmPvStorage,
452 constants.ST_LVM_VG: LvmVgStorage,
453 }
457 """Returns the class for a storage type.
458
459 @type name: string
460 @param name: Storage type
461
462 """
463 try:
464 return _STORAGE_TYPES[name]
465 except KeyError:
466 raise errors.StorageError("Unknown storage type: %r" % name)
467
470 """Factory function for storage methods.
471
472 @type name: string
473 @param name: Storage type
474
475 """
476 return GetStorageClass(name)(*args)
477