Statistics
| Branch: | Tag: | Revision:

root / lib / storage.py @ 6032697c

History | View | Annotate | Download (8.2 kB)

1
#
2
#
3

    
4
# Copyright (C) 2009 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
"""Storage container abstraction.
23

24
"""
25

    
26

    
27
import logging
28

    
29
from ganeti import errors
30
from ganeti import constants
31
from ganeti import utils
32

    
33

    
34
def _ParseSize(value):
35
  return int(round(float(value), 0))
36

    
37

    
38
class _Base:
39
  """Base class for storage abstraction.
40

41
  """
42
  def List(self, name, fields):
43
    """Returns a list of all entities within the storage unit.
44

45
    @type name: string or None
46
    @param name: Entity name or None for all
47
    @type fields: list
48
    @param fields: List with all requested result fields (order is preserved)
49

50
    """
51
    raise NotImplementedError()
52

    
53

    
54
class FileStorage(_Base):
55
  """File storage unit.
56

57
  """
58
  def __init__(self, paths):
59
    """Initializes this class.
60

61
    @type paths: list
62
    @param paths: List of file storage paths
63

64
    """
65
    self._paths = paths
66

    
67
  def List(self, name, fields):
68
    """Returns a list of all entities within the storage unit.
69

70
    See L{_Base.List}.
71

72
    """
73
    rows = []
74

    
75
    if name is None:
76
      paths = self._paths
77
    else:
78
      paths = [name]
79

    
80
    for path in paths:
81
      rows.append(self._ListInner(path, fields))
82

    
83
    return rows
84

    
85
  @staticmethod
86
  def _ListInner(path, fields):
87
    """Gathers requested information from directory.
88

89
    @type path: string
90
    @param path: Path to directory
91
    @type fields: list
92
    @param fields: Requested fields
93

94
    """
95
    values = []
96

    
97
    # Pre-calculate information in case it's requested more than once
98
    if constants.SF_USED in fields:
99
      dirsize = utils.CalculateDirectorySize(path)
100
    else:
101
      dirsize = None
102

    
103
    if constants.SF_FREE in fields:
104
      fsfree = utils.GetFreeFilesystemSpace(path)
105
    else:
106
      fsfree = None
107

    
108
    # Make sure to update constants.VALID_STORAGE_FIELDS when changing fields.
109
    for field_name in fields:
110
      if field_name == constants.SF_NAME:
111
        values.append(path)
112

    
113
      elif field_name == constants.SF_USED:
114
        values.append(dirsize)
115

    
116
      elif field_name == constants.SF_FREE:
117
        values.append(fsfree)
118

    
119
      else:
120
        raise errors.StorageError("Unknown field: %r" % field_name)
121

    
122
    return values
123

    
124

    
125
class _LvmBase(_Base):
126
  """Base class for LVM storage containers.
127

128
  """
129
  LIST_SEP = "|"
130
  LIST_COMMAND = None
131
  LIST_FIELDS = None
132

    
133
  def List(self, name, wanted_field_names):
134
    """Returns a list of all entities within the storage unit.
135

136
    See L{_Base.List}.
137

138
    """
139
    # Get needed LVM fields
140
    lvm_fields = self._GetLvmFields(self.LIST_FIELDS, wanted_field_names)
141

    
142
    # Build LVM command
143
    cmd_args = self._BuildListCommand(self.LIST_COMMAND, self.LIST_SEP,
144
                                      lvm_fields, name)
145

    
146
    # Run LVM command
147
    cmd_result = self._RunListCommand(cmd_args)
148

    
149
    # Split and rearrange LVM command output
150
    return self._BuildList(self._SplitList(cmd_result, self.LIST_SEP,
151
                                           len(lvm_fields)),
152
                           self.LIST_FIELDS,
153
                           wanted_field_names,
154
                           lvm_fields)
155

    
156
  @staticmethod
157
  def _GetLvmFields(fields_def, wanted_field_names):
158
    """Returns unique list of fields wanted from LVM command.
159

160
    @type fields_def: list
161
    @param fields_def: Field definitions
162
    @type wanted_field_names: list
163
    @param wanted_field_names: List of requested fields
164

165
    """
166
    field_to_idx = dict([(field_name, idx)
167
                         for (idx, (field_name, _, _)) in enumerate(fields_def)])
168

    
169
    lvm_fields = []
170

    
171
    for field_name in wanted_field_names:
172
      try:
173
        idx = field_to_idx[field_name]
174
      except IndexError:
175
        raise errors.StorageError("Unknown field: %r" % field_name)
176

    
177
      (_, lvm_name, _) = fields_def[idx]
178

    
179
      lvm_fields.append(lvm_name)
180

    
181
    return utils.UniqueSequence(lvm_fields)
182

    
183
  @classmethod
184
  def _BuildList(cls, cmd_result, fields_def, wanted_field_names, lvm_fields):
185
    """Builds the final result list.
186

187
    @type cmd_result: iterable
188
    @param cmd_result: Iterable of LVM command output (iterable of lists)
189
    @type fields_def: list
190
    @param fields_def: Field definitions
191
    @type wanted_field_names: list
192
    @param wanted_field_names: List of requested fields
193
    @type lvm_fields: list
194
    @param lvm_fields: LVM fields
195

196
    """
197
    lvm_name_to_idx = dict([(lvm_name, idx)
198
                           for (idx, lvm_name) in enumerate(lvm_fields)])
199
    field_to_idx = dict([(field_name, idx)
200
                         for (idx, (field_name, _, _)) in enumerate(fields_def)])
201

    
202
    data = []
203
    for raw_data in cmd_result:
204
      row = []
205

    
206
      for field_name in wanted_field_names:
207
        (_, lvm_name, convert_fn) = fields_def[field_to_idx[field_name]]
208

    
209
        value = raw_data[lvm_name_to_idx[lvm_name]]
210

    
211
        if convert_fn:
212
          value = convert_fn(value)
213

    
214
        row.append(value)
215

    
216
      data.append(row)
217

    
218
    return data
219

    
220
  @staticmethod
221
  def _BuildListCommand(cmd, sep, options, name):
222
    """Builds LVM command line.
223

224
    @type cmd: string
225
    @param cmd: Command name
226
    @type sep: string
227
    @param sep: Field separator character
228
    @type options: list of strings
229
    @param options: Wanted LVM fields
230
    @type name: name or None
231
    @param name: Name of requested entity
232

233
    """
234
    args = [cmd,
235
            "--noheadings", "--units=m", "--nosuffix",
236
            "--separator", sep,
237
            "--options", ",".join(options)]
238

    
239
    if name is not None:
240
      args.append(name)
241

    
242
    return args
243

    
244
  @staticmethod
245
  def _RunListCommand(args):
246
    """Run LVM command.
247

248
    """
249
    result = utils.RunCmd(args)
250

    
251
    if result.failed:
252
      raise errors.StorageError("Failed to run %r, command output: %s" %
253
                                (args[0], result.output))
254

    
255
    return result.stdout
256

    
257
  @staticmethod
258
  def _SplitList(data, sep, fieldcount):
259
    """Splits LVM command output into rows and fields.
260

261
    @type data: string
262
    @param data: LVM command output
263
    @type sep: string
264
    @param sep: Field separator character
265
    @type fieldcount: int
266
    @param fieldcount: Expected number of fields
267

268
    """
269
    for line in data.splitlines():
270
      fields = line.strip().split(sep)
271

    
272
      if len(fields) != fieldcount:
273
        continue
274

    
275
      yield fields
276

    
277

    
278
class LvmPvStorage(_LvmBase):
279
  """LVM Physical Volume storage unit.
280

281
  """
282
  def _GetAllocatable(attr):
283
    if attr:
284
      return (attr[0] == "a")
285
    else:
286
      logging.warning("Invalid PV attribute: %r", attr)
287
      return False
288

    
289
  LIST_COMMAND = "pvs"
290

    
291
  # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
292
  # definitions.
293
  LIST_FIELDS = [
294
    (constants.SF_NAME, "pv_name", None),
295
    (constants.SF_SIZE, "pv_size", _ParseSize),
296
    (constants.SF_USED, "pv_used", _ParseSize),
297
    (constants.SF_FREE, "pv_free", _ParseSize),
298
    (constants.SF_ALLOCATABLE, "pv_attr", _GetAllocatable),
299
    ]
300

    
301

    
302
class LvmVgStorage(_LvmBase):
303
  """LVM Volume Group storage unit.
304

305
  """
306
  LIST_COMMAND = "vgs"
307

    
308
  # Make sure to update constants.VALID_STORAGE_FIELDS when changing field
309
  # definitions.
310
  LIST_FIELDS = [
311
    (constants.SF_NAME, "vg_name", None),
312
    (constants.SF_SIZE, "vg_size", _ParseSize),
313
    ]
314

    
315

    
316
# Lookup table for storage types
317
_STORAGE_TYPES = {
318
  constants.ST_FILE: FileStorage,
319
  constants.ST_LVM_PV: LvmPvStorage,
320
  constants.ST_LVM_VG: LvmVgStorage,
321
  }
322

    
323

    
324
def GetStorageClass(name):
325
  """Returns the class for a storage type.
326

327
  @type name: string
328
  @param name: Storage type
329

330
  """
331
  try:
332
    return _STORAGE_TYPES[name]
333
  except KeyError:
334
    raise errors.StorageError("Unknown storage type: %r" % name)
335

    
336

    
337
def GetStorage(name, *args):
338
  """Factory function for storage methods.
339

340
  @type name: string
341
  @param name: Storage type
342

343
  """
344
  return GetStorageClass(name)(*args)