Statistics
| Branch: | Tag: | Revision:

root / lib / block / base.py @ 47e0abee

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 47e0abee Thomas Thrainer
    @type force: boolean
153 47e0abee Thomas Thrainer

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

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

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

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

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

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

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

190 47e0abee Thomas Thrainer
    @type pause: boolean
191 89ff748d Thomas Thrainer
    @param pause: Whether to pause or resume
192 89ff748d Thomas Thrainer

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

203 89ff748d Thomas Thrainer
    If this device is a mirroring device, this function returns the
204 89ff748d Thomas Thrainer
    status of the mirror.
205 89ff748d Thomas Thrainer

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

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

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

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

219 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
220 89ff748d Thomas Thrainer

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

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

237 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
238 89ff748d Thomas Thrainer

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

279 89ff748d Thomas Thrainer
    Only supported for some device types.
280 89ff748d Thomas Thrainer

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

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

297 89ff748d Thomas Thrainer
    """
298 89ff748d Thomas Thrainer
    raise NotImplementedError
299 89ff748d Thomas Thrainer
300 89ff748d Thomas Thrainer
  def GetActualSize(self):
301 89ff748d Thomas Thrainer
    """Return the actual disk size.
302 89ff748d Thomas Thrainer

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

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

326 89ff748d Thomas Thrainer
  @type msg: string
327 89ff748d Thomas Thrainer
  @param msg: the text of the exception
328 89ff748d Thomas Thrainer
  @raise errors.BlockDeviceError
329 89ff748d Thomas Thrainer

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

340 89ff748d Thomas Thrainer
  This is used in order to simplify the execution of cleanup or
341 89ff748d Thomas Thrainer
  rollback functions.
342 89ff748d Thomas Thrainer

343 89ff748d Thomas Thrainer
  @rtype: boolean
344 89ff748d Thomas Thrainer
  @return: True when fn didn't raise an exception, False otherwise
345 89ff748d Thomas Thrainer

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