Statistics
| Branch: | Tag: | Revision:

root / lib / block / base.py @ d01e51a5

History | View | Annotate | Download (11 kB)

1 89ff748d Thomas Thrainer
#
2 89ff748d Thomas Thrainer
#
3 89ff748d Thomas Thrainer
4 89ff748d Thomas Thrainer
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 89ff748d Thomas Thrainer
#
6 89ff748d Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 89ff748d Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 89ff748d Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 89ff748d Thomas Thrainer
# (at your option) any later version.
10 89ff748d Thomas Thrainer
#
11 89ff748d Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 89ff748d Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 89ff748d Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 89ff748d Thomas Thrainer
# General Public License for more details.
15 89ff748d Thomas Thrainer
#
16 89ff748d Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 89ff748d Thomas Thrainer
# along with this program; if not, write to the Free Software
18 89ff748d Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 89ff748d Thomas Thrainer
# 02110-1301, USA.
20 89ff748d Thomas Thrainer
21 89ff748d Thomas Thrainer
22 89ff748d Thomas Thrainer
"""Block device abstraction - base class and utility functions"""
23 89ff748d Thomas Thrainer
24 89ff748d Thomas Thrainer
import logging
25 89ff748d Thomas Thrainer
26 89ff748d Thomas Thrainer
from ganeti import objects
27 89ff748d Thomas Thrainer
from ganeti import constants
28 89ff748d Thomas Thrainer
from ganeti import utils
29 89ff748d Thomas Thrainer
from ganeti import errors
30 89ff748d Thomas Thrainer
31 89ff748d Thomas Thrainer
32 89ff748d Thomas Thrainer
class BlockDev(object):
33 89ff748d Thomas Thrainer
  """Block device abstract class.
34 89ff748d Thomas Thrainer

35 89ff748d Thomas Thrainer
  A block device can be in the following states:
36 89ff748d Thomas Thrainer
    - not existing on the system, and by `Create()` it goes into:
37 89ff748d Thomas Thrainer
    - existing but not setup/not active, and by `Assemble()` goes into:
38 89ff748d Thomas Thrainer
    - active read-write and by `Open()` it goes into
39 89ff748d Thomas Thrainer
    - online (=used, or ready for use)
40 89ff748d Thomas Thrainer

41 89ff748d Thomas Thrainer
  A device can also be online but read-only, however we are not using
42 89ff748d Thomas Thrainer
  the readonly state (LV has it, if needed in the future) and we are
43 89ff748d Thomas Thrainer
  usually looking at this like at a stack, so it's easier to
44 89ff748d Thomas Thrainer
  conceptualise the transition from not-existing to online and back
45 89ff748d Thomas Thrainer
  like a linear one.
46 89ff748d Thomas Thrainer

47 89ff748d Thomas Thrainer
  The many different states of the device are due to the fact that we
48 89ff748d Thomas Thrainer
  need to cover many device types:
49 89ff748d Thomas Thrainer
    - logical volumes are created, lvchange -a y $lv, and used
50 89ff748d Thomas Thrainer
    - drbd devices are attached to a local disk/remote peer and made primary
51 89ff748d Thomas Thrainer

52 89ff748d Thomas Thrainer
  A block device is identified by three items:
53 89ff748d Thomas Thrainer
    - the /dev path of the device (dynamic)
54 89ff748d Thomas Thrainer
    - a unique ID of the device (static)
55 89ff748d Thomas Thrainer
    - it's major/minor pair (dynamic)
56 89ff748d Thomas Thrainer

57 89ff748d Thomas Thrainer
  Not all devices implement both the first two as distinct items. LVM
58 89ff748d Thomas Thrainer
  logical volumes have their unique ID (the pair volume group, logical
59 89ff748d Thomas Thrainer
  volume name) in a 1-to-1 relation to the dev path. For DRBD devices,
60 89ff748d Thomas Thrainer
  the /dev path is again dynamic and the unique id is the pair (host1,
61 89ff748d Thomas Thrainer
  dev1), (host2, dev2).
62 89ff748d Thomas Thrainer

63 89ff748d Thomas Thrainer
  You can get to a device in two ways:
64 89ff748d Thomas Thrainer
    - creating the (real) device, which returns you
65 89ff748d Thomas Thrainer
      an attached instance (lvcreate)
66 89ff748d Thomas Thrainer
    - attaching of a python instance to an existing (real) device
67 89ff748d Thomas Thrainer

68 89ff748d Thomas Thrainer
  The second point, the attachment to a device, is different
69 89ff748d Thomas Thrainer
  depending on whether the device is assembled or not. At init() time,
70 89ff748d Thomas Thrainer
  we search for a device with the same unique_id as us. If found,
71 89ff748d Thomas Thrainer
  good. It also means that the device is already assembled. If not,
72 89ff748d Thomas Thrainer
  after assembly we'll have our correct major/minor.
73 89ff748d Thomas Thrainer

74 89ff748d Thomas Thrainer
  """
75 89ff748d Thomas Thrainer
  def __init__(self, unique_id, children, size, params):
76 89ff748d Thomas Thrainer
    self._children = children
77 89ff748d Thomas Thrainer
    self.dev_path = None
78 89ff748d Thomas Thrainer
    self.unique_id = unique_id
79 89ff748d Thomas Thrainer
    self.major = None
80 89ff748d Thomas Thrainer
    self.minor = None
81 89ff748d Thomas Thrainer
    self.attached = False
82 89ff748d Thomas Thrainer
    self.size = size
83 89ff748d Thomas Thrainer
    self.params = params
84 89ff748d Thomas Thrainer
85 89ff748d Thomas Thrainer
  def Assemble(self):
86 89ff748d Thomas Thrainer
    """Assemble the device from its components.
87 89ff748d Thomas Thrainer

88 89ff748d Thomas Thrainer
    Implementations of this method by child classes must ensure that:
89 89ff748d Thomas Thrainer
      - after the device has been assembled, it knows its major/minor
90 89ff748d Thomas Thrainer
        numbers; this allows other devices (usually parents) to probe
91 89ff748d Thomas Thrainer
        correctly for their children
92 89ff748d Thomas Thrainer
      - calling this method on an existing, in-use device is safe
93 89ff748d Thomas Thrainer
      - if the device is already configured (and in an OK state),
94 89ff748d Thomas Thrainer
        this method is idempotent
95 89ff748d Thomas Thrainer

96 89ff748d Thomas Thrainer
    """
97 89ff748d Thomas Thrainer
    pass
98 89ff748d Thomas Thrainer
99 89ff748d Thomas Thrainer
  def Attach(self):
100 89ff748d Thomas Thrainer
    """Find a device which matches our config and attach to it.
101 89ff748d Thomas Thrainer

102 89ff748d Thomas Thrainer
    """
103 89ff748d Thomas Thrainer
    raise NotImplementedError
104 89ff748d Thomas Thrainer
105 89ff748d Thomas Thrainer
  def Close(self):
106 89ff748d Thomas Thrainer
    """Notifies that the device will no longer be used for I/O.
107 89ff748d Thomas Thrainer

108 89ff748d Thomas Thrainer
    """
109 89ff748d Thomas Thrainer
    raise NotImplementedError
110 89ff748d Thomas Thrainer
111 89ff748d Thomas Thrainer
  @classmethod
112 89ff748d Thomas Thrainer
  def Create(cls, unique_id, children, size, params, excl_stor):
113 89ff748d Thomas Thrainer
    """Create the device.
114 89ff748d Thomas Thrainer

115 89ff748d Thomas Thrainer
    If the device cannot be created, it will return None
116 89ff748d Thomas Thrainer
    instead. Error messages go to the logging system.
117 89ff748d Thomas Thrainer

118 89ff748d Thomas Thrainer
    Note that for some devices, the unique_id is used, and for other,
119 89ff748d Thomas Thrainer
    the children. The idea is that these two, taken together, are
120 89ff748d Thomas Thrainer
    enough for both creation and assembly (later).
121 89ff748d Thomas Thrainer

122 89ff748d Thomas Thrainer
    """
123 89ff748d Thomas Thrainer
    raise NotImplementedError
124 89ff748d Thomas Thrainer
125 89ff748d Thomas Thrainer
  def Remove(self):
126 89ff748d Thomas Thrainer
    """Remove this device.
127 89ff748d Thomas Thrainer

128 89ff748d Thomas Thrainer
    This makes sense only for some of the device types: LV and file
129 89ff748d Thomas Thrainer
    storage. Also note that if the device can't attach, the removal
130 89ff748d Thomas Thrainer
    can't be completed.
131 89ff748d Thomas Thrainer

132 89ff748d Thomas Thrainer
    """
133 89ff748d Thomas Thrainer
    raise NotImplementedError
134 89ff748d Thomas Thrainer
135 89ff748d Thomas Thrainer
  def Rename(self, new_id):
136 89ff748d Thomas Thrainer
    """Rename this device.
137 89ff748d Thomas Thrainer

138 89ff748d Thomas Thrainer
    This may or may not make sense for a given device type.
139 89ff748d Thomas Thrainer

140 89ff748d Thomas Thrainer
    """
141 89ff748d Thomas Thrainer
    raise NotImplementedError
142 89ff748d Thomas Thrainer
143 89ff748d Thomas Thrainer
  def Open(self, force=False):
144 89ff748d Thomas Thrainer
    """Make the device ready for use.
145 89ff748d Thomas Thrainer

146 89ff748d Thomas Thrainer
    This makes the device ready for I/O. For now, just the DRBD
147 89ff748d Thomas Thrainer
    devices need this.
148 89ff748d Thomas Thrainer

149 89ff748d Thomas Thrainer
    The force parameter signifies that if the device has any kind of
150 89ff748d Thomas Thrainer
    --force thing, it should be used, we know what we are doing.
151 89ff748d Thomas Thrainer

152 89ff748d Thomas Thrainer
    """
153 89ff748d Thomas Thrainer
    raise NotImplementedError
154 89ff748d Thomas Thrainer
155 89ff748d Thomas Thrainer
  def Shutdown(self):
156 89ff748d Thomas Thrainer
    """Shut down the device, freeing its children.
157 89ff748d Thomas Thrainer

158 89ff748d Thomas Thrainer
    This undoes the `Assemble()` work, except for the child
159 89ff748d Thomas Thrainer
    assembling; as such, the children on the device are still
160 89ff748d Thomas Thrainer
    assembled after this call.
161 89ff748d Thomas Thrainer

162 89ff748d Thomas Thrainer
    """
163 89ff748d Thomas Thrainer
    raise NotImplementedError
164 89ff748d Thomas Thrainer
165 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
166 89ff748d Thomas Thrainer
    """Adjust the synchronization parameters of the mirror.
167 89ff748d Thomas Thrainer

168 89ff748d Thomas Thrainer
    In case this is not a mirroring device, this is no-op.
169 89ff748d Thomas Thrainer

170 89ff748d Thomas Thrainer
    @param params: dictionary of LD level disk parameters related to the
171 89ff748d Thomas Thrainer
    synchronization.
172 89ff748d Thomas Thrainer
    @rtype: list
173 89ff748d Thomas Thrainer
    @return: a list of error messages, emitted both by the current node and by
174 89ff748d Thomas Thrainer
    children. An empty list means no errors.
175 89ff748d Thomas Thrainer

176 89ff748d Thomas Thrainer
    """
177 89ff748d Thomas Thrainer
    result = []
178 89ff748d Thomas Thrainer
    if self._children:
179 89ff748d Thomas Thrainer
      for child in self._children:
180 89ff748d Thomas Thrainer
        result.extend(child.SetSyncParams(params))
181 89ff748d Thomas Thrainer
    return result
182 89ff748d Thomas Thrainer
183 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
184 89ff748d Thomas Thrainer
    """Pause/Resume the sync of the mirror.
185 89ff748d Thomas Thrainer

186 89ff748d Thomas Thrainer
    In case this is not a mirroring device, this is no-op.
187 89ff748d Thomas Thrainer

188 89ff748d Thomas Thrainer
    @param pause: Whether to pause or resume
189 89ff748d Thomas Thrainer

190 89ff748d Thomas Thrainer
    """
191 89ff748d Thomas Thrainer
    result = True
192 89ff748d Thomas Thrainer
    if self._children:
193 89ff748d Thomas Thrainer
      for child in self._children:
194 89ff748d Thomas Thrainer
        result = result and child.PauseResumeSync(pause)
195 89ff748d Thomas Thrainer
    return result
196 89ff748d Thomas Thrainer
197 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
198 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
199 89ff748d Thomas Thrainer

200 89ff748d Thomas Thrainer
    If this device is a mirroring device, this function returns the
201 89ff748d Thomas Thrainer
    status of the mirror.
202 89ff748d Thomas Thrainer

203 89ff748d Thomas Thrainer
    If sync_percent is None, it means the device is not syncing.
204 89ff748d Thomas Thrainer

205 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
206 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
207 89ff748d Thomas Thrainer

208 89ff748d Thomas Thrainer
    If is_degraded is True, it means the device is missing
209 89ff748d Thomas Thrainer
    redundancy. This is usually a sign that something went wrong in
210 89ff748d Thomas Thrainer
    the device setup, if sync_percent is None.
211 89ff748d Thomas Thrainer

212 89ff748d Thomas Thrainer
    The ldisk parameter represents the degradation of the local
213 89ff748d Thomas Thrainer
    data. This is only valid for some devices, the rest will always
214 89ff748d Thomas Thrainer
    return False (not degraded).
215 89ff748d Thomas Thrainer

216 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
217 89ff748d Thomas Thrainer

218 89ff748d Thomas Thrainer
    """
219 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
220 89ff748d Thomas Thrainer
                                  major=self.major,
221 89ff748d Thomas Thrainer
                                  minor=self.minor,
222 89ff748d Thomas Thrainer
                                  sync_percent=None,
223 89ff748d Thomas Thrainer
                                  estimated_time=None,
224 89ff748d Thomas Thrainer
                                  is_degraded=False,
225 89ff748d Thomas Thrainer
                                  ldisk_status=constants.LDS_OKAY)
226 89ff748d Thomas Thrainer
227 89ff748d Thomas Thrainer
  def CombinedSyncStatus(self):
228 89ff748d Thomas Thrainer
    """Calculate the mirror status recursively for our children.
229 89ff748d Thomas Thrainer

230 89ff748d Thomas Thrainer
    The return value is the same as for `GetSyncStatus()` except the
231 89ff748d Thomas Thrainer
    minimum percent and maximum time are calculated across our
232 89ff748d Thomas Thrainer
    children.
233 89ff748d Thomas Thrainer

234 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
235 89ff748d Thomas Thrainer

236 89ff748d Thomas Thrainer
    """
237 89ff748d Thomas Thrainer
    status = self.GetSyncStatus()
238 89ff748d Thomas Thrainer
239 89ff748d Thomas Thrainer
    min_percent = status.sync_percent
240 89ff748d Thomas Thrainer
    max_time = status.estimated_time
241 89ff748d Thomas Thrainer
    is_degraded = status.is_degraded
242 89ff748d Thomas Thrainer
    ldisk_status = status.ldisk_status
243 89ff748d Thomas Thrainer
244 89ff748d Thomas Thrainer
    if self._children:
245 89ff748d Thomas Thrainer
      for child in self._children:
246 89ff748d Thomas Thrainer
        child_status = child.GetSyncStatus()
247 89ff748d Thomas Thrainer
248 89ff748d Thomas Thrainer
        if min_percent is None:
249 89ff748d Thomas Thrainer
          min_percent = child_status.sync_percent
250 89ff748d Thomas Thrainer
        elif child_status.sync_percent is not None:
251 89ff748d Thomas Thrainer
          min_percent = min(min_percent, child_status.sync_percent)
252 89ff748d Thomas Thrainer
253 89ff748d Thomas Thrainer
        if max_time is None:
254 89ff748d Thomas Thrainer
          max_time = child_status.estimated_time
255 89ff748d Thomas Thrainer
        elif child_status.estimated_time is not None:
256 89ff748d Thomas Thrainer
          max_time = max(max_time, child_status.estimated_time)
257 89ff748d Thomas Thrainer
258 89ff748d Thomas Thrainer
        is_degraded = is_degraded or child_status.is_degraded
259 89ff748d Thomas Thrainer
260 89ff748d Thomas Thrainer
        if ldisk_status is None:
261 89ff748d Thomas Thrainer
          ldisk_status = child_status.ldisk_status
262 89ff748d Thomas Thrainer
        elif child_status.ldisk_status is not None:
263 89ff748d Thomas Thrainer
          ldisk_status = max(ldisk_status, child_status.ldisk_status)
264 89ff748d Thomas Thrainer
265 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
266 89ff748d Thomas Thrainer
                                  major=self.major,
267 89ff748d Thomas Thrainer
                                  minor=self.minor,
268 89ff748d Thomas Thrainer
                                  sync_percent=min_percent,
269 89ff748d Thomas Thrainer
                                  estimated_time=max_time,
270 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
271 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
272 89ff748d Thomas Thrainer
273 89ff748d Thomas Thrainer
  def SetInfo(self, text):
274 89ff748d Thomas Thrainer
    """Update metadata with info text.
275 89ff748d Thomas Thrainer

276 89ff748d Thomas Thrainer
    Only supported for some device types.
277 89ff748d Thomas Thrainer

278 89ff748d Thomas Thrainer
    """
279 89ff748d Thomas Thrainer
    for child in self._children:
280 89ff748d Thomas Thrainer
      child.SetInfo(text)
281 89ff748d Thomas Thrainer
282 89ff748d Thomas Thrainer
  def Grow(self, amount, dryrun, backingstore):
283 89ff748d Thomas Thrainer
    """Grow the block device.
284 89ff748d Thomas Thrainer

285 89ff748d Thomas Thrainer
    @type amount: integer
286 89ff748d Thomas Thrainer
    @param amount: the amount (in mebibytes) to grow with
287 89ff748d Thomas Thrainer
    @type dryrun: boolean
288 89ff748d Thomas Thrainer
    @param dryrun: whether to execute the operation in simulation mode
289 89ff748d Thomas Thrainer
        only, without actually increasing the size
290 89ff748d Thomas Thrainer
    @param backingstore: whether to execute the operation on backing storage
291 89ff748d Thomas Thrainer
        only, or on "logical" storage only; e.g. DRBD is logical storage,
292 89ff748d Thomas Thrainer
        whereas LVM, file, RBD are backing storage
293 89ff748d Thomas Thrainer

294 89ff748d Thomas Thrainer
    """
295 89ff748d Thomas Thrainer
    raise NotImplementedError
296 89ff748d Thomas Thrainer
297 89ff748d Thomas Thrainer
  def GetActualSize(self):
298 89ff748d Thomas Thrainer
    """Return the actual disk size.
299 89ff748d Thomas Thrainer

300 89ff748d Thomas Thrainer
    @note: the device needs to be active when this is called
301 89ff748d Thomas Thrainer

302 89ff748d Thomas Thrainer
    """
303 89ff748d Thomas Thrainer
    assert self.attached, "BlockDevice not attached in GetActualSize()"
304 89ff748d Thomas Thrainer
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
305 89ff748d Thomas Thrainer
    if result.failed:
306 89ff748d Thomas Thrainer
      ThrowError("blockdev failed (%s): %s",
307 89ff748d Thomas Thrainer
                  result.fail_reason, result.output)
308 89ff748d Thomas Thrainer
    try:
309 89ff748d Thomas Thrainer
      sz = int(result.output.strip())
310 89ff748d Thomas Thrainer
    except (ValueError, TypeError), err:
311 89ff748d Thomas Thrainer
      ThrowError("Failed to parse blockdev output: %s", str(err))
312 89ff748d Thomas Thrainer
    return sz
313 89ff748d Thomas Thrainer
314 89ff748d Thomas Thrainer
  def __repr__(self):
315 89ff748d Thomas Thrainer
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
316 89ff748d Thomas Thrainer
            (self.__class__, self.unique_id, self._children,
317 89ff748d Thomas Thrainer
             self.major, self.minor, self.dev_path))
318 89ff748d Thomas Thrainer
319 89ff748d Thomas Thrainer
320 89ff748d Thomas Thrainer
def ThrowError(msg, *args):
321 89ff748d Thomas Thrainer
  """Log an error to the node daemon and the raise an exception.
322 89ff748d Thomas Thrainer

323 89ff748d Thomas Thrainer
  @type msg: string
324 89ff748d Thomas Thrainer
  @param msg: the text of the exception
325 89ff748d Thomas Thrainer
  @raise errors.BlockDeviceError
326 89ff748d Thomas Thrainer

327 89ff748d Thomas Thrainer
  """
328 89ff748d Thomas Thrainer
  if args:
329 89ff748d Thomas Thrainer
    msg = msg % args
330 89ff748d Thomas Thrainer
  logging.error(msg)
331 89ff748d Thomas Thrainer
  raise errors.BlockDeviceError(msg)
332 89ff748d Thomas Thrainer
333 89ff748d Thomas Thrainer
334 89ff748d Thomas Thrainer
def IgnoreError(fn, *args, **kwargs):
335 89ff748d Thomas Thrainer
  """Executes the given function, ignoring BlockDeviceErrors.
336 89ff748d Thomas Thrainer

337 89ff748d Thomas Thrainer
  This is used in order to simplify the execution of cleanup or
338 89ff748d Thomas Thrainer
  rollback functions.
339 89ff748d Thomas Thrainer

340 89ff748d Thomas Thrainer
  @rtype: boolean
341 89ff748d Thomas Thrainer
  @return: True when fn didn't raise an exception, False otherwise
342 89ff748d Thomas Thrainer

343 89ff748d Thomas Thrainer
  """
344 89ff748d Thomas Thrainer
  try:
345 89ff748d Thomas Thrainer
    fn(*args, **kwargs)
346 89ff748d Thomas Thrainer
    return True
347 89ff748d Thomas Thrainer
  except errors.BlockDeviceError, err:
348 89ff748d Thomas Thrainer
    logging.warning("Caught BlockDeviceError but ignoring: %s", str(err))
349 89ff748d Thomas Thrainer
    return False