Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 67d101d4

History | View | Annotate | Download (58 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Block device abstraction"""
23 a8083063 Iustin Pop
24 a8083063 Iustin Pop
import re
25 a8083063 Iustin Pop
import time
26 a8083063 Iustin Pop
import errno
27 a2cfdea2 Iustin Pop
import pyparsing as pyp
28 6f695a2e Manuel Franceschini
import os
29 468c5f77 Iustin Pop
import logging
30 a8083063 Iustin Pop
31 a8083063 Iustin Pop
from ganeti import utils
32 a8083063 Iustin Pop
from ganeti import errors
33 fe96220b Iustin Pop
from ganeti import constants
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
36 82463074 Iustin Pop
def _IgnoreError(fn, *args, **kwargs):
37 82463074 Iustin Pop
  """Executes the given function, ignoring BlockDeviceErrors.
38 82463074 Iustin Pop

39 82463074 Iustin Pop
  This is used in order to simplify the execution of cleanup or
40 82463074 Iustin Pop
  rollback functions.
41 82463074 Iustin Pop

42 82463074 Iustin Pop
  @rtype: boolean
43 82463074 Iustin Pop
  @return: True when fn didn't raise an exception, False otherwise
44 82463074 Iustin Pop

45 82463074 Iustin Pop
  """
46 82463074 Iustin Pop
  try:
47 82463074 Iustin Pop
    fn(*args, **kwargs)
48 82463074 Iustin Pop
    return True
49 82463074 Iustin Pop
  except errors.BlockDeviceError, err:
50 82463074 Iustin Pop
    logging.warning("Caught BlockDeviceError but ignoring: %s" % str(err))
51 82463074 Iustin Pop
    return False
52 82463074 Iustin Pop
53 82463074 Iustin Pop
54 82463074 Iustin Pop
def _ThrowError(msg, *args):
55 82463074 Iustin Pop
  """Log an error to the node daemon and the raise an exception.
56 82463074 Iustin Pop

57 82463074 Iustin Pop
  @type msg: string
58 82463074 Iustin Pop
  @param msg: the text of the exception
59 82463074 Iustin Pop
  @raise errors.BlockDeviceError
60 82463074 Iustin Pop

61 82463074 Iustin Pop
  """
62 82463074 Iustin Pop
  if args:
63 82463074 Iustin Pop
    msg = msg % args
64 82463074 Iustin Pop
  logging.error(msg)
65 82463074 Iustin Pop
  raise errors.BlockDeviceError(msg)
66 82463074 Iustin Pop
67 82463074 Iustin Pop
68 a8083063 Iustin Pop
class BlockDev(object):
69 a8083063 Iustin Pop
  """Block device abstract class.
70 a8083063 Iustin Pop

71 a8083063 Iustin Pop
  A block device can be in the following states:
72 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
73 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
74 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
75 a8083063 Iustin Pop
    - online (=used, or ready for use)
76 a8083063 Iustin Pop

77 a8083063 Iustin Pop
  A device can also be online but read-only, however we are not using
78 abdf0113 Iustin Pop
  the readonly state (LV has it, if needed in the future) and we are
79 abdf0113 Iustin Pop
  usually looking at this like at a stack, so it's easier to
80 abdf0113 Iustin Pop
  conceptualise the transition from not-existing to online and back
81 a8083063 Iustin Pop
  like a linear one.
82 a8083063 Iustin Pop

83 a8083063 Iustin Pop
  The many different states of the device are due to the fact that we
84 a8083063 Iustin Pop
  need to cover many device types:
85 a8083063 Iustin Pop
    - logical volumes are created, lvchange -a y $lv, and used
86 a8083063 Iustin Pop
    - drbd devices are attached to a local disk/remote peer and made primary
87 a8083063 Iustin Pop

88 a8083063 Iustin Pop
  A block device is identified by three items:
89 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
90 a8083063 Iustin Pop
    - a unique ID of the device (static)
91 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
92 a8083063 Iustin Pop

93 a8083063 Iustin Pop
  Not all devices implement both the first two as distinct items. LVM
94 a8083063 Iustin Pop
  logical volumes have their unique ID (the pair volume group, logical
95 abdf0113 Iustin Pop
  volume name) in a 1-to-1 relation to the dev path. For DRBD devices,
96 abdf0113 Iustin Pop
  the /dev path is again dynamic and the unique id is the pair (host1,
97 abdf0113 Iustin Pop
  dev1), (host2, dev2).
98 a8083063 Iustin Pop

99 a8083063 Iustin Pop
  You can get to a device in two ways:
100 a8083063 Iustin Pop
    - creating the (real) device, which returns you
101 abdf0113 Iustin Pop
      an attached instance (lvcreate)
102 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
103 a8083063 Iustin Pop

104 a8083063 Iustin Pop
  The second point, the attachement to a device, is different
105 a8083063 Iustin Pop
  depending on whether the device is assembled or not. At init() time,
106 a8083063 Iustin Pop
  we search for a device with the same unique_id as us. If found,
107 a8083063 Iustin Pop
  good. It also means that the device is already assembled. If not,
108 a8083063 Iustin Pop
  after assembly we'll have our correct major/minor.
109 a8083063 Iustin Pop

110 a8083063 Iustin Pop
  """
111 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
112 a8083063 Iustin Pop
    self._children = children
113 a8083063 Iustin Pop
    self.dev_path = None
114 a8083063 Iustin Pop
    self.unique_id = unique_id
115 a8083063 Iustin Pop
    self.major = None
116 a8083063 Iustin Pop
    self.minor = None
117 cb999543 Iustin Pop
    self.attached = False
118 464f8daf Iustin Pop
    self.size = size
119 a8083063 Iustin Pop
120 a8083063 Iustin Pop
  def Assemble(self):
121 a8083063 Iustin Pop
    """Assemble the device from its components.
122 a8083063 Iustin Pop

123 f87548b5 Iustin Pop
    Implementations of this method by child classes must ensure that:
124 f87548b5 Iustin Pop
      - after the device has been assembled, it knows its major/minor
125 f87548b5 Iustin Pop
        numbers; this allows other devices (usually parents) to probe
126 f87548b5 Iustin Pop
        correctly for their children
127 f87548b5 Iustin Pop
      - calling this method on an existing, in-use device is safe
128 f87548b5 Iustin Pop
      - if the device is already configured (and in an OK state),
129 f87548b5 Iustin Pop
        this method is idempotent
130 a8083063 Iustin Pop

131 a8083063 Iustin Pop
    """
132 1063abd1 Iustin Pop
    pass
133 a8083063 Iustin Pop
134 a8083063 Iustin Pop
  def Attach(self):
135 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
136 a8083063 Iustin Pop

137 a8083063 Iustin Pop
    """
138 a8083063 Iustin Pop
    raise NotImplementedError
139 a8083063 Iustin Pop
140 a8083063 Iustin Pop
  def Close(self):
141 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
142 a8083063 Iustin Pop

143 a8083063 Iustin Pop
    """
144 a8083063 Iustin Pop
    raise NotImplementedError
145 a8083063 Iustin Pop
146 a8083063 Iustin Pop
  @classmethod
147 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
148 a8083063 Iustin Pop
    """Create the device.
149 a8083063 Iustin Pop

150 a8083063 Iustin Pop
    If the device cannot be created, it will return None
151 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
152 a8083063 Iustin Pop

153 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
154 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
155 a8083063 Iustin Pop
    enough for both creation and assembly (later).
156 a8083063 Iustin Pop

157 a8083063 Iustin Pop
    """
158 a8083063 Iustin Pop
    raise NotImplementedError
159 a8083063 Iustin Pop
160 a8083063 Iustin Pop
  def Remove(self):
161 a8083063 Iustin Pop
    """Remove this device.
162 a8083063 Iustin Pop

163 abdf0113 Iustin Pop
    This makes sense only for some of the device types: LV and file
164 abdf0113 Iustin Pop
    storeage. Also note that if the device can't attach, the removal
165 abdf0113 Iustin Pop
    can't be completed.
166 a8083063 Iustin Pop

167 a8083063 Iustin Pop
    """
168 a8083063 Iustin Pop
    raise NotImplementedError
169 a8083063 Iustin Pop
170 f3e513ad Iustin Pop
  def Rename(self, new_id):
171 f3e513ad Iustin Pop
    """Rename this device.
172 f3e513ad Iustin Pop

173 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
174 f3e513ad Iustin Pop

175 f3e513ad Iustin Pop
    """
176 f3e513ad Iustin Pop
    raise NotImplementedError
177 f3e513ad Iustin Pop
178 a8083063 Iustin Pop
  def Open(self, force=False):
179 a8083063 Iustin Pop
    """Make the device ready for use.
180 a8083063 Iustin Pop

181 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
182 a8083063 Iustin Pop
    devices need this.
183 a8083063 Iustin Pop

184 a8083063 Iustin Pop
    The force parameter signifies that if the device has any kind of
185 a8083063 Iustin Pop
    --force thing, it should be used, we know what we are doing.
186 a8083063 Iustin Pop

187 a8083063 Iustin Pop
    """
188 a8083063 Iustin Pop
    raise NotImplementedError
189 a8083063 Iustin Pop
190 a8083063 Iustin Pop
  def Shutdown(self):
191 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
192 a8083063 Iustin Pop

193 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
194 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
195 a8083063 Iustin Pop
    assembled after this call.
196 a8083063 Iustin Pop

197 a8083063 Iustin Pop
    """
198 a8083063 Iustin Pop
    raise NotImplementedError
199 a8083063 Iustin Pop
200 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
201 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
    In case this is not a mirroring device, this is no-op.
204 a8083063 Iustin Pop

205 a8083063 Iustin Pop
    """
206 a8083063 Iustin Pop
    result = True
207 a8083063 Iustin Pop
    if self._children:
208 a8083063 Iustin Pop
      for child in self._children:
209 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
210 a8083063 Iustin Pop
    return result
211 a8083063 Iustin Pop
212 a8083063 Iustin Pop
  def GetSyncStatus(self):
213 a8083063 Iustin Pop
    """Returns the sync status of the device.
214 a8083063 Iustin Pop

215 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
216 a8083063 Iustin Pop
    status of the mirror.
217 a8083063 Iustin Pop

218 0834c866 Iustin Pop
    If sync_percent is None, it means the device is not syncing.
219 a8083063 Iustin Pop

220 a8083063 Iustin Pop
    If estimated_time is None, it means we can't estimate
221 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
222 0834c866 Iustin Pop

223 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
224 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
225 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
226 a8083063 Iustin Pop

227 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
228 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
229 0834c866 Iustin Pop
    return False (not degraded).
230 0834c866 Iustin Pop

231 c41eea6e Iustin Pop
    @rtype: tuple
232 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
233 c41eea6e Iustin Pop

234 a8083063 Iustin Pop
    """
235 0834c866 Iustin Pop
    return None, None, False, False
236 a8083063 Iustin Pop
237 a8083063 Iustin Pop
238 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
239 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
240 a8083063 Iustin Pop

241 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
242 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
243 a8083063 Iustin Pop
    children.
244 a8083063 Iustin Pop

245 a8083063 Iustin Pop
    """
246 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
247 a8083063 Iustin Pop
    if self._children:
248 a8083063 Iustin Pop
      for child in self._children:
249 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
250 a8083063 Iustin Pop
        if min_percent is None:
251 a8083063 Iustin Pop
          min_percent = c_percent
252 a8083063 Iustin Pop
        elif c_percent is not None:
253 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
254 a8083063 Iustin Pop
        if max_time is None:
255 a8083063 Iustin Pop
          max_time = c_time
256 a8083063 Iustin Pop
        elif c_time is not None:
257 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
258 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
259 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
260 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
263 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
264 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
265 a0c3fea1 Michael Hanselmann

266 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
267 a0c3fea1 Michael Hanselmann

268 a0c3fea1 Michael Hanselmann
    """
269 a0c3fea1 Michael Hanselmann
    for child in self._children:
270 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
271 a0c3fea1 Michael Hanselmann
272 1005d816 Iustin Pop
  def Grow(self, amount):
273 1005d816 Iustin Pop
    """Grow the block device.
274 1005d816 Iustin Pop

275 c41eea6e Iustin Pop
    @param amount: the amount (in mebibytes) to grow with
276 1005d816 Iustin Pop

277 1005d816 Iustin Pop
    """
278 1005d816 Iustin Pop
    raise NotImplementedError
279 a0c3fea1 Michael Hanselmann
280 fcff3897 Iustin Pop
  def GetActualSize(self):
281 fcff3897 Iustin Pop
    """Return the actual disk size.
282 fcff3897 Iustin Pop

283 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
284 fcff3897 Iustin Pop

285 fcff3897 Iustin Pop
    """
286 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
287 fcff3897 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize64", self.dev_path])
288 fcff3897 Iustin Pop
    if result.failed:
289 fcff3897 Iustin Pop
      _ThrowError("blockdev failed (%s): %s",
290 fcff3897 Iustin Pop
                  result.fail_reason, result.output)
291 fcff3897 Iustin Pop
    try:
292 fcff3897 Iustin Pop
      sz = int(result.output.strip())
293 fcff3897 Iustin Pop
    except (ValueError, TypeError), err:
294 fcff3897 Iustin Pop
      _ThrowError("Failed to parse blockdev output: %s", str(err))
295 fcff3897 Iustin Pop
    return sz
296 fcff3897 Iustin Pop
297 a8083063 Iustin Pop
  def __repr__(self):
298 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
299 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
300 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
301 a8083063 Iustin Pop
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
304 a8083063 Iustin Pop
  """Logical Volume block device.
305 a8083063 Iustin Pop

306 a8083063 Iustin Pop
  """
307 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
308 a8083063 Iustin Pop
    """Attaches to a LV device.
309 a8083063 Iustin Pop

310 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
311 a8083063 Iustin Pop

312 a8083063 Iustin Pop
    """
313 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
314 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
315 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
316 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
317 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
318 99e8295c Iustin Pop
    self._degraded = True
319 38256320 Iustin Pop
    self.major = self.minor = self.pe_size = self.stripe_count = None
320 a8083063 Iustin Pop
    self.Attach()
321 a8083063 Iustin Pop
322 a8083063 Iustin Pop
  @classmethod
323 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
324 a8083063 Iustin Pop
    """Create a new logical volume.
325 a8083063 Iustin Pop

326 a8083063 Iustin Pop
    """
327 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
328 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
329 6c626518 Iustin Pop
                                   str(unique_id))
330 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
331 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
332 a8083063 Iustin Pop
    if not pvs_info:
333 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", vg_name)
334 a8083063 Iustin Pop
    pvs_info.sort()
335 a8083063 Iustin Pop
    pvs_info.reverse()
336 5b7b5d49 Guido Trotter
337 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
338 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
339 fecbe9d5 Iustin Pop
    current_pvs = len(pvlist)
340 fecbe9d5 Iustin Pop
    stripes = min(current_pvs, constants.LVM_STRIPECOUNT)
341 5b7b5d49 Guido Trotter
342 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
343 5b7b5d49 Guido Trotter
    # calling the create function.
344 a8083063 Iustin Pop
    if free_size < size:
345 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
346 82463074 Iustin Pop
                  " available %s", size, free_size)
347 fecbe9d5 Iustin Pop
    cmd = ["lvcreate", "-L%dm" % size, "-n%s" % lv_name]
348 fecbe9d5 Iustin Pop
    # If the free space is not well distributed, we won't be able to
349 fecbe9d5 Iustin Pop
    # create an optimally-striped volume; in that case, we want to try
350 fecbe9d5 Iustin Pop
    # with N, N-1, ..., 2, and finally 1 (non-stripped) number of
351 fecbe9d5 Iustin Pop
    # stripes
352 fecbe9d5 Iustin Pop
    for stripes_arg in range(stripes, 0, -1):
353 fecbe9d5 Iustin Pop
      result = utils.RunCmd(cmd + ["-i%d" % stripes_arg] + [vg_name] + pvlist)
354 fecbe9d5 Iustin Pop
      if not result.failed:
355 fecbe9d5 Iustin Pop
        break
356 a8083063 Iustin Pop
    if result.failed:
357 82463074 Iustin Pop
      _ThrowError("LV create failed (%s): %s",
358 82463074 Iustin Pop
                  result.fail_reason, result.output)
359 464f8daf Iustin Pop
    return LogicalVolume(unique_id, children, size)
360 a8083063 Iustin Pop
361 a8083063 Iustin Pop
  @staticmethod
362 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
363 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
364 a8083063 Iustin Pop

365 c41eea6e Iustin Pop
    @param vg_name: the volume group name
366 a8083063 Iustin Pop

367 c41eea6e Iustin Pop
    @rtype: list
368 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
369 098c0958 Michael Hanselmann

370 a8083063 Iustin Pop
    """
371 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
372 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
373 a8083063 Iustin Pop
               "--separator=:"]
374 a8083063 Iustin Pop
    result = utils.RunCmd(command)
375 a8083063 Iustin Pop
    if result.failed:
376 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
377 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
378 a8083063 Iustin Pop
      return None
379 a8083063 Iustin Pop
    data = []
380 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
381 a8083063 Iustin Pop
      fields = line.strip().split(':')
382 a8083063 Iustin Pop
      if len(fields) != 4:
383 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
384 a8083063 Iustin Pop
        return None
385 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
386 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
387 a8083063 Iustin Pop
        continue
388 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
389 a8083063 Iustin Pop
390 a8083063 Iustin Pop
    return data
391 a8083063 Iustin Pop
392 a8083063 Iustin Pop
  def Remove(self):
393 a8083063 Iustin Pop
    """Remove this logical volume.
394 a8083063 Iustin Pop

395 a8083063 Iustin Pop
    """
396 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
397 a8083063 Iustin Pop
      # the LV does not exist
398 0c6c04ec Iustin Pop
      return
399 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
400 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
401 a8083063 Iustin Pop
    if result.failed:
402 0c6c04ec Iustin Pop
      _ThrowError("Can't lvremove: %s - %s", result.fail_reason, result.output)
403 a8083063 Iustin Pop
404 f3e513ad Iustin Pop
  def Rename(self, new_id):
405 f3e513ad Iustin Pop
    """Rename this logical volume.
406 f3e513ad Iustin Pop

407 f3e513ad Iustin Pop
    """
408 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
409 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
410 f3e513ad Iustin Pop
    new_vg, new_name = new_id
411 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
412 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
413 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
414 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
415 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
416 f3e513ad Iustin Pop
    if result.failed:
417 82463074 Iustin Pop
      _ThrowError("Failed to rename the logical volume: %s", result.output)
418 be345db0 Iustin Pop
    self._lv_name = new_name
419 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
420 be345db0 Iustin Pop
421 a8083063 Iustin Pop
  def Attach(self):
422 a8083063 Iustin Pop
    """Attach to an existing LV.
423 a8083063 Iustin Pop

424 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
425 c99a3cc0 Manuel Franceschini
    which matches our name. If so, its major/minor will be
426 a8083063 Iustin Pop
    recorded.
427 a8083063 Iustin Pop

428 a8083063 Iustin Pop
    """
429 cb999543 Iustin Pop
    self.attached = False
430 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
431 38256320 Iustin Pop
                           "--units=m", "--nosuffix",
432 38256320 Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor,"
433 38256320 Iustin Pop
                           "vg_extent_size,stripes", self.dev_path])
434 a8083063 Iustin Pop
    if result.failed:
435 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
436 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
437 a8083063 Iustin Pop
      return False
438 38256320 Iustin Pop
    # the output can (and will) have multiple lines for multi-segment
439 38256320 Iustin Pop
    # LVs, as the 'stripes' parameter is a segment one, so we take
440 38256320 Iustin Pop
    # only the last entry, which is the one we're interested in; note
441 38256320 Iustin Pop
    # that with LVM2 anyway the 'stripes' value must be constant
442 38256320 Iustin Pop
    # across segments, so this is a no-op actually
443 38256320 Iustin Pop
    out = result.stdout.splitlines()
444 38256320 Iustin Pop
    if not out: # totally empty result? splitlines() returns at least
445 38256320 Iustin Pop
                # one line for any non-empty string
446 38256320 Iustin Pop
      logging.error("Can't parse LVS output, no lines? Got '%s'", str(out))
447 38256320 Iustin Pop
      return False
448 38256320 Iustin Pop
    out = out[-1].strip().rstrip(',')
449 99e8295c Iustin Pop
    out = out.split(",")
450 38256320 Iustin Pop
    if len(out) != 5:
451 38256320 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 5", str(out))
452 99e8295c Iustin Pop
      return False
453 99e8295c Iustin Pop
454 38256320 Iustin Pop
    status, major, minor, pe_size, stripes = out
455 99e8295c Iustin Pop
    if len(status) != 6:
456 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
457 99e8295c Iustin Pop
      return False
458 99e8295c Iustin Pop
459 99e8295c Iustin Pop
    try:
460 99e8295c Iustin Pop
      major = int(major)
461 99e8295c Iustin Pop
      minor = int(minor)
462 99e8295c Iustin Pop
    except ValueError, err:
463 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
464 99e8295c Iustin Pop
465 38256320 Iustin Pop
    try:
466 38256320 Iustin Pop
      pe_size = int(float(pe_size))
467 38256320 Iustin Pop
    except (TypeError, ValueError), err:
468 38256320 Iustin Pop
      logging.error("Can't parse vg extent size: %s", err)
469 38256320 Iustin Pop
      return False
470 38256320 Iustin Pop
471 38256320 Iustin Pop
    try:
472 38256320 Iustin Pop
      stripes = int(stripes)
473 38256320 Iustin Pop
    except (TypeError, ValueError), err:
474 38256320 Iustin Pop
      logging.error("Can't parse the number of stripes: %s", err)
475 38256320 Iustin Pop
      return False
476 38256320 Iustin Pop
477 99e8295c Iustin Pop
    self.major = major
478 99e8295c Iustin Pop
    self.minor = minor
479 38256320 Iustin Pop
    self.pe_size = pe_size
480 38256320 Iustin Pop
    self.stripe_count = stripes
481 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
482 99e8295c Iustin Pop
                                      # storage
483 cb999543 Iustin Pop
    self.attached = True
484 99e8295c Iustin Pop
    return True
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  def Assemble(self):
487 a8083063 Iustin Pop
    """Assemble the device.
488 a8083063 Iustin Pop

489 5574047a Iustin Pop
    We alway run `lvchange -ay` on the LV to ensure it's active before
490 5574047a Iustin Pop
    use, as there were cases when xenvg was not active after boot
491 5574047a Iustin Pop
    (also possibly after disk issues).
492 a8083063 Iustin Pop

493 a8083063 Iustin Pop
    """
494 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
495 5574047a Iustin Pop
    if result.failed:
496 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
497 a8083063 Iustin Pop
498 a8083063 Iustin Pop
  def Shutdown(self):
499 a8083063 Iustin Pop
    """Shutdown the device.
500 a8083063 Iustin Pop

501 a8083063 Iustin Pop
    This is a no-op for the LV device type, as we don't deactivate the
502 a8083063 Iustin Pop
    volumes on shutdown.
503 a8083063 Iustin Pop

504 a8083063 Iustin Pop
    """
505 746f7476 Iustin Pop
    pass
506 a8083063 Iustin Pop
507 9db6dbce Iustin Pop
  def GetSyncStatus(self):
508 9db6dbce Iustin Pop
    """Returns the sync status of the device.
509 9db6dbce Iustin Pop

510 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
511 9db6dbce Iustin Pop
    status of the mirror.
512 9db6dbce Iustin Pop

513 9db6dbce Iustin Pop
    For logical volumes, sync_percent and estimated_time are always
514 9db6dbce Iustin Pop
    None (no recovery in progress, as we don't handle the mirrored LV
515 0834c866 Iustin Pop
    case). The is_degraded parameter is the inverse of the ldisk
516 0834c866 Iustin Pop
    parameter.
517 9db6dbce Iustin Pop

518 0834c866 Iustin Pop
    For the ldisk parameter, we check if the logical volume has the
519 0834c866 Iustin Pop
    'virtual' type, which means it's not backed by existing storage
520 0834c866 Iustin Pop
    anymore (read from it return I/O error). This happens after a
521 0834c866 Iustin Pop
    physical disk failure and subsequent 'vgreduce --removemissing' on
522 0834c866 Iustin Pop
    the volume group.
523 9db6dbce Iustin Pop

524 99e8295c Iustin Pop
    The status was already read in Attach, so we just return it.
525 99e8295c Iustin Pop

526 c41eea6e Iustin Pop
    @rtype: tuple
527 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
528 c41eea6e Iustin Pop

529 9db6dbce Iustin Pop
    """
530 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
531 9db6dbce Iustin Pop
532 a8083063 Iustin Pop
  def Open(self, force=False):
533 a8083063 Iustin Pop
    """Make the device ready for I/O.
534 a8083063 Iustin Pop

535 a8083063 Iustin Pop
    This is a no-op for the LV device type.
536 a8083063 Iustin Pop

537 a8083063 Iustin Pop
    """
538 fdbd668d Iustin Pop
    pass
539 a8083063 Iustin Pop
540 a8083063 Iustin Pop
  def Close(self):
541 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
542 a8083063 Iustin Pop

543 a8083063 Iustin Pop
    This is a no-op for the LV device type.
544 a8083063 Iustin Pop

545 a8083063 Iustin Pop
    """
546 fdbd668d Iustin Pop
    pass
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
  def Snapshot(self, size):
549 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
550 a8083063 Iustin Pop

551 a8083063 Iustin Pop
    """
552 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
553 a8083063 Iustin Pop
554 a8083063 Iustin Pop
    # remove existing snapshot if found
555 464f8daf Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None, size)
556 0c6c04ec Iustin Pop
    _IgnoreError(snap.Remove)
557 a8083063 Iustin Pop
558 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
559 a8083063 Iustin Pop
    if not pvs_info:
560 82463074 Iustin Pop
      _ThrowError("Can't compute PV info for vg %s", self._vg_name)
561 a8083063 Iustin Pop
    pvs_info.sort()
562 a8083063 Iustin Pop
    pvs_info.reverse()
563 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
564 a8083063 Iustin Pop
    if free_size < size:
565 82463074 Iustin Pop
      _ThrowError("Not enough free space: required %s,"
566 82463074 Iustin Pop
                  " available %s", size, free_size)
567 a8083063 Iustin Pop
568 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
569 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
570 a8083063 Iustin Pop
    if result.failed:
571 82463074 Iustin Pop
      _ThrowError("command: %s error: %s - %s",
572 82463074 Iustin Pop
                  result.cmd, result.fail_reason, result.output)
573 a8083063 Iustin Pop
574 a8083063 Iustin Pop
    return snap_name
575 a8083063 Iustin Pop
576 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
577 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
578 a0c3fea1 Michael Hanselmann

579 a0c3fea1 Michael Hanselmann
    """
580 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
581 a0c3fea1 Michael Hanselmann
582 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
583 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
584 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
585 a0c3fea1 Michael Hanselmann
586 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
587 a0c3fea1 Michael Hanselmann
    text = text[:128]
588 a0c3fea1 Michael Hanselmann
589 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
590 a0c3fea1 Michael Hanselmann
                           self.dev_path])
591 a0c3fea1 Michael Hanselmann
    if result.failed:
592 82463074 Iustin Pop
      _ThrowError("Command: %s error: %s - %s", result.cmd, result.fail_reason,
593 82463074 Iustin Pop
                  result.output)
594 82463074 Iustin Pop
595 1005d816 Iustin Pop
  def Grow(self, amount):
596 1005d816 Iustin Pop
    """Grow the logical volume.
597 1005d816 Iustin Pop

598 1005d816 Iustin Pop
    """
599 38256320 Iustin Pop
    if self.pe_size is None or self.stripe_count is None:
600 38256320 Iustin Pop
      if not self.Attach():
601 38256320 Iustin Pop
        _ThrowError("Can't attach to LV during Grow()")
602 38256320 Iustin Pop
    full_stripe_size = self.pe_size * self.stripe_count
603 38256320 Iustin Pop
    rest = amount % full_stripe_size
604 38256320 Iustin Pop
    if rest != 0:
605 38256320 Iustin Pop
      amount += full_stripe_size - rest
606 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
607 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
608 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
609 1005d816 Iustin Pop
    # supports 'cling'
610 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
611 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
612 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
613 1005d816 Iustin Pop
      if not result.failed:
614 1005d816 Iustin Pop
        return
615 82463074 Iustin Pop
    _ThrowError("Can't grow LV %s: %s", self.dev_path, result.output)
616 a0c3fea1 Michael Hanselmann
617 a0c3fea1 Michael Hanselmann
618 6b90c22e Iustin Pop
class DRBD8Status(object):
619 6b90c22e Iustin Pop
  """A DRBD status representation class.
620 6b90c22e Iustin Pop

621 6b90c22e Iustin Pop
  Note that this doesn't support unconfigured devices (cs:Unconfigured).
622 6b90c22e Iustin Pop

623 6b90c22e Iustin Pop
  """
624 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
625 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
626 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
627 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
628 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
629 6b90c22e Iustin Pop
630 3c003d9d Iustin Pop
  CS_UNCONFIGURED = "Unconfigured"
631 3c003d9d Iustin Pop
  CS_STANDALONE = "StandAlone"
632 3c003d9d Iustin Pop
  CS_WFCONNECTION = "WFConnection"
633 3c003d9d Iustin Pop
  CS_WFREPORTPARAMS = "WFReportParams"
634 3c003d9d Iustin Pop
  CS_CONNECTED = "Connected"
635 3c003d9d Iustin Pop
  CS_STARTINGSYNCS = "StartingSyncS"
636 3c003d9d Iustin Pop
  CS_STARTINGSYNCT = "StartingSyncT"
637 3c003d9d Iustin Pop
  CS_WFBITMAPS = "WFBitMapS"
638 3c003d9d Iustin Pop
  CS_WFBITMAPT = "WFBitMapT"
639 3c003d9d Iustin Pop
  CS_WFSYNCUUID = "WFSyncUUID"
640 3c003d9d Iustin Pop
  CS_SYNCSOURCE = "SyncSource"
641 3c003d9d Iustin Pop
  CS_SYNCTARGET = "SyncTarget"
642 3c003d9d Iustin Pop
  CS_PAUSEDSYNCS = "PausedSyncS"
643 3c003d9d Iustin Pop
  CS_PAUSEDSYNCT = "PausedSyncT"
644 3c003d9d Iustin Pop
  CSET_SYNC = frozenset([
645 3c003d9d Iustin Pop
    CS_WFREPORTPARAMS,
646 3c003d9d Iustin Pop
    CS_STARTINGSYNCS,
647 3c003d9d Iustin Pop
    CS_STARTINGSYNCT,
648 3c003d9d Iustin Pop
    CS_WFBITMAPS,
649 3c003d9d Iustin Pop
    CS_WFBITMAPT,
650 3c003d9d Iustin Pop
    CS_WFSYNCUUID,
651 3c003d9d Iustin Pop
    CS_SYNCSOURCE,
652 3c003d9d Iustin Pop
    CS_SYNCTARGET,
653 3c003d9d Iustin Pop
    CS_PAUSEDSYNCS,
654 3c003d9d Iustin Pop
    CS_PAUSEDSYNCT,
655 3c003d9d Iustin Pop
    ])
656 3c003d9d Iustin Pop
657 3c003d9d Iustin Pop
  DS_DISKLESS = "Diskless"
658 3c003d9d Iustin Pop
  DS_ATTACHING = "Attaching" # transient state
659 3c003d9d Iustin Pop
  DS_FAILED = "Failed" # transient state, next: diskless
660 3c003d9d Iustin Pop
  DS_NEGOTIATING = "Negotiating" # transient state
661 3c003d9d Iustin Pop
  DS_INCONSISTENT = "Inconsistent" # while syncing or after creation
662 3c003d9d Iustin Pop
  DS_OUTDATED = "Outdated"
663 3c003d9d Iustin Pop
  DS_DUNKNOWN = "DUnknown" # shown for peer disk when not connected
664 3c003d9d Iustin Pop
  DS_CONSISTENT = "Consistent"
665 3c003d9d Iustin Pop
  DS_UPTODATE = "UpToDate" # normal state
666 3c003d9d Iustin Pop
667 3c003d9d Iustin Pop
  RO_PRIMARY = "Primary"
668 3c003d9d Iustin Pop
  RO_SECONDARY = "Secondary"
669 3c003d9d Iustin Pop
  RO_UNKNOWN = "Unknown"
670 3c003d9d Iustin Pop
671 6b90c22e Iustin Pop
  def __init__(self, procline):
672 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
673 767d52d3 Iustin Pop
    if u:
674 3c003d9d Iustin Pop
      self.cstatus = self.CS_UNCONFIGURED
675 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
676 767d52d3 Iustin Pop
    else:
677 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
678 767d52d3 Iustin Pop
      if not m:
679 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
680 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
681 767d52d3 Iustin Pop
      self.lrole = m.group(2)
682 767d52d3 Iustin Pop
      self.rrole = m.group(3)
683 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
684 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
685 767d52d3 Iustin Pop
686 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
687 6b90c22e Iustin Pop
688 3c003d9d Iustin Pop
    self.is_standalone = self.cstatus == self.CS_STANDALONE
689 3c003d9d Iustin Pop
    self.is_wfconn = self.cstatus == self.CS_WFCONNECTION
690 3c003d9d Iustin Pop
    self.is_connected = self.cstatus == self.CS_CONNECTED
691 3c003d9d Iustin Pop
    self.is_primary = self.lrole == self.RO_PRIMARY
692 3c003d9d Iustin Pop
    self.is_secondary = self.lrole == self.RO_SECONDARY
693 3c003d9d Iustin Pop
    self.peer_primary = self.rrole == self.RO_PRIMARY
694 3c003d9d Iustin Pop
    self.peer_secondary = self.rrole == self.RO_SECONDARY
695 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
696 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
697 6b90c22e Iustin Pop
698 3c003d9d Iustin Pop
    self.is_diskless = self.ldisk == self.DS_DISKLESS
699 3c003d9d Iustin Pop
    self.is_disk_uptodate = self.ldisk == self.DS_UPTODATE
700 6b90c22e Iustin Pop
701 3c003d9d Iustin Pop
    self.is_in_resync = self.cstatus in self.CSET_SYNC
702 3c003d9d Iustin Pop
    self.is_in_use = self.cstatus != self.CS_UNCONFIGURED
703 6b93ec9d Iustin Pop
704 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
705 6b90c22e Iustin Pop
    if m:
706 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
707 6b90c22e Iustin Pop
      hours = int(m.group(2))
708 6b90c22e Iustin Pop
      minutes = int(m.group(3))
709 6b90c22e Iustin Pop
      seconds = int(m.group(4))
710 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
711 6b90c22e Iustin Pop
    else:
712 3c003d9d Iustin Pop
      # we have (in this if branch) no percent information, but if
713 3c003d9d Iustin Pop
      # we're resyncing we need to 'fake' a sync percent information,
714 3c003d9d Iustin Pop
      # as this is how cmdlib determines if it makes sense to wait for
715 3c003d9d Iustin Pop
      # resyncing or not
716 3c003d9d Iustin Pop
      if self.is_in_resync:
717 3c003d9d Iustin Pop
        self.sync_percent = 0
718 3c003d9d Iustin Pop
      else:
719 3c003d9d Iustin Pop
        self.sync_percent = None
720 6b90c22e Iustin Pop
      self.est_time = None
721 6b90c22e Iustin Pop
722 6b90c22e Iustin Pop
723 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
724 0f7f32d9 Iustin Pop
  """Base DRBD class.
725 a8083063 Iustin Pop

726 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
727 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
728 0f7f32d9 Iustin Pop

729 abdf0113 Iustin Pop
  """
730 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
731 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
732 a8083063 Iustin Pop
733 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
734 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
735 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
736 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
737 a8083063 Iustin Pop
738 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
739 6b90c22e Iustin Pop
740 abdf0113 Iustin Pop
  @staticmethod
741 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
742 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
743 a8083063 Iustin Pop

744 a8083063 Iustin Pop
    """
745 abdf0113 Iustin Pop
    try:
746 f6eaed12 Iustin Pop
      stat = open(filename, "r")
747 f6eaed12 Iustin Pop
      try:
748 f6eaed12 Iustin Pop
        data = stat.read().splitlines()
749 f6eaed12 Iustin Pop
      finally:
750 f6eaed12 Iustin Pop
        stat.close()
751 f6eaed12 Iustin Pop
    except EnvironmentError, err:
752 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
753 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
754 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
755 f6eaed12 Iustin Pop
      else:
756 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
757 abdf0113 Iustin Pop
    if not data:
758 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
759 abdf0113 Iustin Pop
    return data
760 a8083063 Iustin Pop
761 abdf0113 Iustin Pop
  @staticmethod
762 abdf0113 Iustin Pop
  def _MassageProcData(data):
763 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
764 a8083063 Iustin Pop

765 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
766 c41eea6e Iustin Pop
        for that minor
767 a8083063 Iustin Pop

768 a8083063 Iustin Pop
    """
769 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
770 abdf0113 Iustin Pop
    results = {}
771 abdf0113 Iustin Pop
    old_minor = old_line = None
772 abdf0113 Iustin Pop
    for line in data:
773 67d101d4 Iustin Pop
      if not line: # completely empty lines, as can be returned by drbd8.0+
774 67d101d4 Iustin Pop
        continue
775 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
776 abdf0113 Iustin Pop
      if lresult is not None:
777 abdf0113 Iustin Pop
        if old_minor is not None:
778 abdf0113 Iustin Pop
          results[old_minor] = old_line
779 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
780 abdf0113 Iustin Pop
        old_line = line
781 abdf0113 Iustin Pop
      else:
782 abdf0113 Iustin Pop
        if old_minor is not None:
783 abdf0113 Iustin Pop
          old_line += " " + line.strip()
784 abdf0113 Iustin Pop
    # add last line
785 abdf0113 Iustin Pop
    if old_minor is not None:
786 abdf0113 Iustin Pop
      results[old_minor] = old_line
787 abdf0113 Iustin Pop
    return results
788 a8083063 Iustin Pop
789 abdf0113 Iustin Pop
  @classmethod
790 abdf0113 Iustin Pop
  def _GetVersion(cls):
791 abdf0113 Iustin Pop
    """Return the DRBD version.
792 a8083063 Iustin Pop

793 abdf0113 Iustin Pop
    This will return a dict with keys:
794 c41eea6e Iustin Pop
      - k_major
795 c41eea6e Iustin Pop
      - k_minor
796 c41eea6e Iustin Pop
      - k_point
797 c41eea6e Iustin Pop
      - api
798 c41eea6e Iustin Pop
      - proto
799 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
800 a8083063 Iustin Pop

801 a8083063 Iustin Pop
    """
802 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
803 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
804 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
805 abdf0113 Iustin Pop
    if not version:
806 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
807 abdf0113 Iustin Pop
                                    first_line)
808 a8083063 Iustin Pop
809 abdf0113 Iustin Pop
    values = version.groups()
810 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
811 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
812 abdf0113 Iustin Pop
              'k_point': int(values[2]),
813 abdf0113 Iustin Pop
              'api': int(values[3]),
814 abdf0113 Iustin Pop
              'proto': int(values[4]),
815 abdf0113 Iustin Pop
             }
816 abdf0113 Iustin Pop
    if values[5] is not None:
817 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
818 a8083063 Iustin Pop
819 abdf0113 Iustin Pop
    return retval
820 abdf0113 Iustin Pop
821 abdf0113 Iustin Pop
  @staticmethod
822 abdf0113 Iustin Pop
  def _DevPath(minor):
823 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
824 a8083063 Iustin Pop

825 a8083063 Iustin Pop
    """
826 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
827 a8083063 Iustin Pop
828 abdf0113 Iustin Pop
  @classmethod
829 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
830 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
831 a8083063 Iustin Pop

832 a8083063 Iustin Pop
    """
833 abdf0113 Iustin Pop
    data = cls._GetProcData()
834 a8083063 Iustin Pop
835 abdf0113 Iustin Pop
    used_devs = {}
836 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
837 abdf0113 Iustin Pop
    for line in data:
838 abdf0113 Iustin Pop
      match = valid_line.match(line)
839 abdf0113 Iustin Pop
      if not match:
840 abdf0113 Iustin Pop
        continue
841 abdf0113 Iustin Pop
      minor = int(match.group(1))
842 abdf0113 Iustin Pop
      state = match.group(2)
843 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
844 abdf0113 Iustin Pop
        continue
845 abdf0113 Iustin Pop
      used_devs[minor] = state, line
846 a8083063 Iustin Pop
847 abdf0113 Iustin Pop
    return used_devs
848 a8083063 Iustin Pop
849 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
850 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
851 0834c866 Iustin Pop

852 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
853 a8083063 Iustin Pop

854 a8083063 Iustin Pop
    """
855 abdf0113 Iustin Pop
    if minor is None:
856 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
857 cb999543 Iustin Pop
      self.attached = False
858 a8083063 Iustin Pop
    else:
859 abdf0113 Iustin Pop
      self.minor = minor
860 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
861 cb999543 Iustin Pop
      self.attached = True
862 a8083063 Iustin Pop
863 a8083063 Iustin Pop
  @staticmethod
864 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
865 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
866 a8083063 Iustin Pop

867 abdf0113 Iustin Pop
    This currently only check the size, which must be around
868 abdf0113 Iustin Pop
    128MiB.
869 a8083063 Iustin Pop

870 a8083063 Iustin Pop
    """
871 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
872 abdf0113 Iustin Pop
    if result.failed:
873 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
874 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
875 a8083063 Iustin Pop
    try:
876 abdf0113 Iustin Pop
      sectors = int(result.stdout)
877 abdf0113 Iustin Pop
    except ValueError:
878 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
879 abdf0113 Iustin Pop
    bytes = sectors * 512
880 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
881 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
882 1dc10972 Iustin Pop
    # the maximum *valid* size of the meta device when living on top
883 1dc10972 Iustin Pop
    # of LVM is hard to compute: it depends on the number of stripes
884 1dc10972 Iustin Pop
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
885 1dc10972 Iustin Pop
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
886 1dc10972 Iustin Pop
    # size meta device; as such, we restrict it to 1GB (a little bit
887 1dc10972 Iustin Pop
    # too generous, but making assumptions about PE size is hard)
888 1dc10972 Iustin Pop
    if bytes > 1024 * 1024 * 1024:
889 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
890 a8083063 Iustin Pop
891 abdf0113 Iustin Pop
  def Rename(self, new_id):
892 abdf0113 Iustin Pop
    """Rename a device.
893 a8083063 Iustin Pop

894 abdf0113 Iustin Pop
    This is not supported for drbd devices.
895 a8083063 Iustin Pop

896 a8083063 Iustin Pop
    """
897 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
898 a8083063 Iustin Pop
899 f3e513ad Iustin Pop
900 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
901 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
902 a2cfdea2 Iustin Pop

903 a2cfdea2 Iustin Pop
  This implements the local host part of the DRBD device, i.e. it
904 a2cfdea2 Iustin Pop
  doesn't do anything to the supposed peer. If you need a fully
905 a2cfdea2 Iustin Pop
  connected DRBD pair, you need to use this class on both hosts.
906 a2cfdea2 Iustin Pop

907 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
908 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
909 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
910 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
911 a2cfdea2 Iustin Pop

912 a2cfdea2 Iustin Pop
  """
913 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
914 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
915 a2cfdea2 Iustin Pop
916 cf8df3f3 Iustin Pop
  # timeout constants
917 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
918 cf8df3f3 Iustin Pop
919 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
920 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
921 fc1dc9d7 Iustin Pop
      children = []
922 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
923 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
924 c3f9340c Guido Trotter
    version = self._GetVersion()
925 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
926 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
927 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
928 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
929 a2cfdea2 Iustin Pop
930 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
931 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
932 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
933 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
934 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
935 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
936 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
937 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
938 ffa1c0dc Iustin Pop
        self._lport == self._rport):
939 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
940 ffa1c0dc Iustin Pop
                       (unique_id,))
941 a2cfdea2 Iustin Pop
    self.Attach()
942 a2cfdea2 Iustin Pop
943 a2cfdea2 Iustin Pop
  @classmethod
944 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
945 a2cfdea2 Iustin Pop
    """Initialize a meta device.
946 a2cfdea2 Iustin Pop

947 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
948 a2cfdea2 Iustin Pop

949 a2cfdea2 Iustin Pop
    """
950 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
951 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
952 a2cfdea2 Iustin Pop
    if result.failed:
953 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
954 a2cfdea2 Iustin Pop
955 a2cfdea2 Iustin Pop
  @classmethod
956 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
957 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
958 a2cfdea2 Iustin Pop

959 a2cfdea2 Iustin Pop
    This is specific to 8.x as the minors are allocated dynamically,
960 a2cfdea2 Iustin Pop
    so non-existing numbers up to a max minor count are actually free.
961 a2cfdea2 Iustin Pop

962 a2cfdea2 Iustin Pop
    """
963 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
964 a2cfdea2 Iustin Pop
965 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
966 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
967 a2cfdea2 Iustin Pop
    highest = None
968 a2cfdea2 Iustin Pop
    for line in data:
969 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
970 a2cfdea2 Iustin Pop
      if match:
971 a2cfdea2 Iustin Pop
        return int(match.group(1))
972 a2cfdea2 Iustin Pop
      match = used_line.match(line)
973 a2cfdea2 Iustin Pop
      if match:
974 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
975 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
976 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
977 a2cfdea2 Iustin Pop
      return 0
978 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
979 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
980 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
981 a2cfdea2 Iustin Pop
    return highest + 1
982 a2cfdea2 Iustin Pop
983 a2cfdea2 Iustin Pop
  @classmethod
984 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
985 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
986 a2cfdea2 Iustin Pop

987 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
988 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
989 a2cfdea2 Iustin Pop

990 a2cfdea2 Iustin Pop
    """
991 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
992 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
993 a2cfdea2 Iustin Pop
994 a2cfdea2 Iustin Pop
    # pyparsing setup
995 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
996 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
997 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
998 a2cfdea2 Iustin Pop
    # this also converts the value to an int
999 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1000 a2cfdea2 Iustin Pop
1001 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1002 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1003 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1004 a2cfdea2 Iustin Pop
1005 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1006 a2cfdea2 Iustin Pop
1007 a2cfdea2 Iustin Pop
    # value types
1008 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1009 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1010 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
1011 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
1012 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
1013 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
1014 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1015 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1016 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1017 01e2ce3a Iustin Pop
    # device name, extended syntax
1018 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
1019 a2cfdea2 Iustin Pop
1020 a2cfdea2 Iustin Pop
    # a statement
1021 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1022 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
1023 01e2ce3a Iustin Pop
                         device_value) +
1024 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1025 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1026 a2cfdea2 Iustin Pop
1027 a2cfdea2 Iustin Pop
    # an entire section
1028 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1029 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1030 a2cfdea2 Iustin Pop
1031 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1032 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1033 a2cfdea2 Iustin Pop
1034 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1035 a2cfdea2 Iustin Pop
1036 a2cfdea2 Iustin Pop
    return bnf
1037 a2cfdea2 Iustin Pop
1038 a2cfdea2 Iustin Pop
  @classmethod
1039 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1040 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1041 a2cfdea2 Iustin Pop

1042 a2cfdea2 Iustin Pop
    """
1043 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1044 a2cfdea2 Iustin Pop
    if result.failed:
1045 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
1046 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1047 3840729d Iustin Pop
      return None
1048 3840729d Iustin Pop
    return result.stdout
1049 3840729d Iustin Pop
1050 3840729d Iustin Pop
  @classmethod
1051 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1052 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1053 3840729d Iustin Pop

1054 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1055 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1056 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1057 3840729d Iustin Pop
    by _GetShowData.
1058 3840729d Iustin Pop

1059 3840729d Iustin Pop
    """
1060 3840729d Iustin Pop
    data = {}
1061 a2cfdea2 Iustin Pop
    if not out:
1062 a2cfdea2 Iustin Pop
      return data
1063 a2cfdea2 Iustin Pop
1064 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1065 a2cfdea2 Iustin Pop
    # run pyparse
1066 a2cfdea2 Iustin Pop
1067 a2cfdea2 Iustin Pop
    try:
1068 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1069 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1070 82463074 Iustin Pop
      _ThrowError("Can't parse drbdsetup show output: %s", str(err))
1071 a2cfdea2 Iustin Pop
1072 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1073 a2cfdea2 Iustin Pop
    for section in results:
1074 a2cfdea2 Iustin Pop
      sname = section[0]
1075 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1076 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1077 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1078 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1079 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1080 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1081 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1082 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1083 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1084 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1085 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1086 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1087 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1088 a2cfdea2 Iustin Pop
    return data
1089 a2cfdea2 Iustin Pop
1090 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1091 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1092 a2cfdea2 Iustin Pop

1093 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1094 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1095 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1096 a2cfdea2 Iustin Pop
    device.
1097 a2cfdea2 Iustin Pop

1098 a2cfdea2 Iustin Pop
    """
1099 b00b95dd Iustin Pop
    if self._children:
1100 b00b95dd Iustin Pop
      backend, meta = self._children
1101 b00b95dd Iustin Pop
    else:
1102 b00b95dd Iustin Pop
      backend = meta = None
1103 b00b95dd Iustin Pop
1104 a2cfdea2 Iustin Pop
    if backend is not None:
1105 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1106 a2cfdea2 Iustin Pop
    else:
1107 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1108 b00b95dd Iustin Pop
1109 a2cfdea2 Iustin Pop
    if meta is not None:
1110 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1111 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1112 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1113 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1114 a2cfdea2 Iustin Pop
    else:
1115 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1116 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1117 a2cfdea2 Iustin Pop
    return retval
1118 a2cfdea2 Iustin Pop
1119 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1120 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1121 a2cfdea2 Iustin Pop

1122 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1123 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1124 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1125 a2cfdea2 Iustin Pop
    device.
1126 a2cfdea2 Iustin Pop

1127 a2cfdea2 Iustin Pop
    """
1128 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1129 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1130 a2cfdea2 Iustin Pop
      return True
1131 a2cfdea2 Iustin Pop
1132 a2cfdea2 Iustin Pop
    if self._lhost is None:
1133 a2cfdea2 Iustin Pop
      return False
1134 a2cfdea2 Iustin Pop
1135 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1136 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1137 a2cfdea2 Iustin Pop
      return False
1138 a2cfdea2 Iustin Pop
1139 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1140 a2cfdea2 Iustin Pop
    retval = (retval and
1141 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1142 a2cfdea2 Iustin Pop
    return retval
1143 a2cfdea2 Iustin Pop
1144 a2cfdea2 Iustin Pop
  @classmethod
1145 f069addf Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta, size):
1146 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1147 a2cfdea2 Iustin Pop

1148 a2cfdea2 Iustin Pop
    """
1149 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1150 f069addf Iustin Pop
            backend, meta, "0",
1151 f069addf Iustin Pop
            "-e", "detach",
1152 f069addf Iustin Pop
            "--create-device"]
1153 60bca04a Iustin Pop
    if size:
1154 60bca04a Iustin Pop
      args.extend(["-d", "%sm" % size])
1155 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1156 a2cfdea2 Iustin Pop
    if result.failed:
1157 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1158 a2cfdea2 Iustin Pop
1159 a2cfdea2 Iustin Pop
  @classmethod
1160 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1161 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1162 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1163 a2cfdea2 Iustin Pop

1164 a2cfdea2 Iustin Pop
    """
1165 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1166 52857176 Iustin Pop
    if None in net_info:
1167 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1168 52857176 Iustin Pop
      # sure its shutdown
1169 1063abd1 Iustin Pop
      cls._ShutdownNet(minor)
1170 1063abd1 Iustin Pop
      return
1171 52857176 Iustin Pop
1172 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1173 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1174 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1175 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1176 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1177 7d585316 Iustin Pop
    # about its peer.
1178 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1179 7d585316 Iustin Pop
1180 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1181 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1182 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1183 f38478b2 Iustin Pop
            "-B", "consensus",
1184 ab6cc81c Iustin Pop
            "--create-device",
1185 f38478b2 Iustin Pop
            ]
1186 a2cfdea2 Iustin Pop
    if dual_pri:
1187 a2cfdea2 Iustin Pop
      args.append("-m")
1188 a2cfdea2 Iustin Pop
    if hmac and secret:
1189 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1190 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1191 a2cfdea2 Iustin Pop
    if result.failed:
1192 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't setup network: %s - %s",
1193 1063abd1 Iustin Pop
                  minor, result.fail_reason, result.output)
1194 a2cfdea2 Iustin Pop
1195 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1196 a2cfdea2 Iustin Pop
    ok = False
1197 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1198 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1199 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1200 a2cfdea2 Iustin Pop
        time.sleep(1)
1201 a2cfdea2 Iustin Pop
        continue
1202 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1203 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1204 a2cfdea2 Iustin Pop
        time.sleep(1)
1205 a2cfdea2 Iustin Pop
        continue
1206 a2cfdea2 Iustin Pop
      ok = True
1207 a2cfdea2 Iustin Pop
      break
1208 a2cfdea2 Iustin Pop
    if not ok:
1209 1063abd1 Iustin Pop
      _ThrowError("drbd%d: timeout while configuring network", minor)
1210 a2cfdea2 Iustin Pop
1211 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1212 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1213 b00b95dd Iustin Pop

1214 b00b95dd Iustin Pop
    """
1215 b00b95dd Iustin Pop
    if self.minor is None:
1216 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1217 82463074 Iustin Pop
                  self._aminor)
1218 b00b95dd Iustin Pop
    if len(devices) != 2:
1219 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1220 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1221 03ece5f3 Iustin Pop
    if "local_dev" in info:
1222 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1223 b00b95dd Iustin Pop
    backend, meta = devices
1224 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1225 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1226 b00b95dd Iustin Pop
    backend.Open()
1227 b00b95dd Iustin Pop
    meta.Open()
1228 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1229 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1230 b00b95dd Iustin Pop
1231 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1232 b00b95dd Iustin Pop
    self._children = devices
1233 b00b95dd Iustin Pop
1234 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1235 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1236 b00b95dd Iustin Pop

1237 b00b95dd Iustin Pop
    """
1238 b00b95dd Iustin Pop
    if self.minor is None:
1239 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1240 82463074 Iustin Pop
                  self._aminor)
1241 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1242 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1243 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1244 03ece5f3 Iustin Pop
      return
1245 b00b95dd Iustin Pop
    if len(self._children) != 2:
1246 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1247 82463074 Iustin Pop
                  self._children)
1248 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1249 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1250 e739bd57 Iustin Pop
      return
1251 b00b95dd Iustin Pop
    if len(devices) != 2:
1252 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1253 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1254 e739bd57 Iustin Pop
      if dev != child.dev_path:
1255 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1256 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1257 b00b95dd Iustin Pop
1258 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1259 b00b95dd Iustin Pop
    self._children = []
1260 b00b95dd Iustin Pop
1261 7d585316 Iustin Pop
  @classmethod
1262 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1263 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1264 a2cfdea2 Iustin Pop

1265 7d585316 Iustin Pop
    This is the low-level implementation.
1266 7d585316 Iustin Pop

1267 7d585316 Iustin Pop
    @type minor: int
1268 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1269 7d585316 Iustin Pop
    @type kbytes: int
1270 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1271 7d585316 Iustin Pop
    @rtype: boolean
1272 7d585316 Iustin Pop
    @return: the success of the operation
1273 7d585316 Iustin Pop

1274 a2cfdea2 Iustin Pop
    """
1275 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1276 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1277 a2cfdea2 Iustin Pop
    if result.failed:
1278 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1279 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1280 7d585316 Iustin Pop
    return not result.failed
1281 7d585316 Iustin Pop
1282 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1283 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1284 7d585316 Iustin Pop

1285 7d585316 Iustin Pop
    @type kbytes: int
1286 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1287 7d585316 Iustin Pop
    @rtype: boolean
1288 7d585316 Iustin Pop
    @return: the success of the operation
1289 7d585316 Iustin Pop

1290 7d585316 Iustin Pop
    """
1291 7d585316 Iustin Pop
    if self.minor is None:
1292 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1293 7d585316 Iustin Pop
      return False
1294 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1295 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1296 a2cfdea2 Iustin Pop
1297 6b90c22e Iustin Pop
  def GetProcStatus(self):
1298 6b90c22e Iustin Pop
    """Return device data from /proc.
1299 6b90c22e Iustin Pop

1300 6b90c22e Iustin Pop
    """
1301 6b90c22e Iustin Pop
    if self.minor is None:
1302 82463074 Iustin Pop
      _ThrowError("drbd%d: GetStats() called while not attached", self._aminor)
1303 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1304 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1305 82463074 Iustin Pop
      _ThrowError("drbd%d: can't find myself in /proc", self.minor)
1306 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1307 6b90c22e Iustin Pop
1308 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1309 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1310 a2cfdea2 Iustin Pop

1311 a2cfdea2 Iustin Pop

1312 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1313 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1314 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1315 0834c866 Iustin Pop

1316 0834c866 Iustin Pop

1317 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1318 0834c866 Iustin Pop
    network not connected or local disk missing.
1319 0834c866 Iustin Pop

1320 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1321 0834c866 Iustin Pop
    disk or not.
1322 a2cfdea2 Iustin Pop

1323 c41eea6e Iustin Pop
    @rtype: tuple
1324 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
1325 c41eea6e Iustin Pop

1326 a2cfdea2 Iustin Pop
    """
1327 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1328 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1329 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1330 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1331 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1332 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1333 a2cfdea2 Iustin Pop
1334 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1335 a2cfdea2 Iustin Pop
    """Make the local state primary.
1336 a2cfdea2 Iustin Pop

1337 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1338 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1339 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1340 f860ff4e Guido Trotter
    is mandatory.
1341 a2cfdea2 Iustin Pop

1342 a2cfdea2 Iustin Pop
    """
1343 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1344 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1345 a2cfdea2 Iustin Pop
      return False
1346 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1347 a2cfdea2 Iustin Pop
    if force:
1348 a2cfdea2 Iustin Pop
      cmd.append("-o")
1349 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1350 a2cfdea2 Iustin Pop
    if result.failed:
1351 82463074 Iustin Pop
      _ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
1352 82463074 Iustin Pop
                  result.output)
1353 a2cfdea2 Iustin Pop
1354 a2cfdea2 Iustin Pop
  def Close(self):
1355 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1356 a2cfdea2 Iustin Pop

1357 a2cfdea2 Iustin Pop
    This will, of course, fail if the device is in use.
1358 a2cfdea2 Iustin Pop

1359 a2cfdea2 Iustin Pop
    """
1360 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1361 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1362 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1363 a2cfdea2 Iustin Pop
    if result.failed:
1364 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1365 82463074 Iustin Pop
                  self.minor, result.output)
1366 a2cfdea2 Iustin Pop
1367 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1368 cf8df3f3 Iustin Pop
    """Removes network configuration.
1369 cf8df3f3 Iustin Pop

1370 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1371 cf8df3f3 Iustin Pop

1372 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1373 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1374 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1375 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1376 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1377 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1378 cf8df3f3 Iustin Pop
    exception.
1379 cf8df3f3 Iustin Pop

1380 cf8df3f3 Iustin Pop
    """
1381 cf8df3f3 Iustin Pop
    if self.minor is None:
1382 82463074 Iustin Pop
      _ThrowError("drbd%d: disk not attached in re-attach net", self._aminor)
1383 cf8df3f3 Iustin Pop
1384 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1385 82463074 Iustin Pop
      _ThrowError("drbd%d: DRBD disk missing network info in"
1386 82463074 Iustin Pop
                  " DisconnectNet()", self.minor)
1387 cf8df3f3 Iustin Pop
1388 1063abd1 Iustin Pop
    ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor)
1389 cf8df3f3 Iustin Pop
    timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
1390 cf8df3f3 Iustin Pop
    sleep_time = 0.100 # we start the retry time at 100 miliseconds
1391 cf8df3f3 Iustin Pop
    while time.time() < timeout_limit:
1392 cf8df3f3 Iustin Pop
      status = self.GetProcStatus()
1393 cf8df3f3 Iustin Pop
      if status.is_standalone:
1394 cf8df3f3 Iustin Pop
        break
1395 cf8df3f3 Iustin Pop
      # retry the disconnect, it seems possible that due to a
1396 cf8df3f3 Iustin Pop
      # well-time disconnect on the peer, my disconnect command might
1397 cf8df3f3 Iustin Pop
      # be ingored and forgotten
1398 1063abd1 Iustin Pop
      ever_disconnected = _IgnoreError(self._ShutdownNet, self.minor) or \
1399 1063abd1 Iustin Pop
                          ever_disconnected
1400 cf8df3f3 Iustin Pop
      time.sleep(sleep_time)
1401 cf8df3f3 Iustin Pop
      sleep_time = min(2, sleep_time * 1.5)
1402 cf8df3f3 Iustin Pop
1403 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1404 cf8df3f3 Iustin Pop
      if ever_disconnected:
1405 82463074 Iustin Pop
        msg = ("drbd%d: device did not react to the"
1406 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1407 cf8df3f3 Iustin Pop
      else:
1408 82463074 Iustin Pop
        msg = "drbd%d: can't shutdown network, even after multiple retries"
1409 82463074 Iustin Pop
      _ThrowError(msg, self.minor)
1410 cf8df3f3 Iustin Pop
1411 cf8df3f3 Iustin Pop
    reconfig_time = time.time() - timeout_limit + self._NET_RECONFIG_TIMEOUT
1412 cf8df3f3 Iustin Pop
    if reconfig_time > 15: # hardcoded alert limit
1413 82463074 Iustin Pop
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
1414 82463074 Iustin Pop
                   self.minor, reconfig_time)
1415 cf8df3f3 Iustin Pop
1416 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1417 cf8df3f3 Iustin Pop
    """Reconnects the network.
1418 cf8df3f3 Iustin Pop

1419 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1420 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1421 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1422 cf8df3f3 Iustin Pop

1423 cf8df3f3 Iustin Pop
    Args:
1424 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1425 cf8df3f3 Iustin Pop

1426 cf8df3f3 Iustin Pop
    """
1427 cf8df3f3 Iustin Pop
    if self.minor is None:
1428 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1429 cf8df3f3 Iustin Pop
1430 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1431 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1432 cf8df3f3 Iustin Pop
1433 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1434 cf8df3f3 Iustin Pop
1435 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1436 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1437 cf8df3f3 Iustin Pop
1438 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1439 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1440 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1441 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1442 cf8df3f3 Iustin Pop
1443 a2cfdea2 Iustin Pop
  def Attach(self):
1444 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1445 2d0c8319 Iustin Pop

1446 2d0c8319 Iustin Pop
    This doesn't do any device configurations - it only checks if the
1447 2d0c8319 Iustin Pop
    minor is in a state different from Unconfigured.
1448 2d0c8319 Iustin Pop

1449 2d0c8319 Iustin Pop
    Note that this function will not change the state of the system in
1450 2d0c8319 Iustin Pop
    any way (except in case of side-effects caused by reading from
1451 2d0c8319 Iustin Pop
    /proc).
1452 2d0c8319 Iustin Pop

1453 2d0c8319 Iustin Pop
    """
1454 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1455 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1456 2d0c8319 Iustin Pop
      minor = self._aminor
1457 2d0c8319 Iustin Pop
    else:
1458 2d0c8319 Iustin Pop
      minor = None
1459 2d0c8319 Iustin Pop
1460 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1461 2d0c8319 Iustin Pop
    return minor is not None
1462 2d0c8319 Iustin Pop
1463 2d0c8319 Iustin Pop
  def Assemble(self):
1464 2d0c8319 Iustin Pop
    """Assemble the drbd.
1465 2d0c8319 Iustin Pop

1466 2d0c8319 Iustin Pop
    Method:
1467 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1468 2d0c8319 Iustin Pop
        our config
1469 2d0c8319 Iustin Pop
      - if not, we create it from zero
1470 2d0c8319 Iustin Pop

1471 2d0c8319 Iustin Pop
    """
1472 1063abd1 Iustin Pop
    super(DRBD8, self).Assemble()
1473 2d0c8319 Iustin Pop
1474 2d0c8319 Iustin Pop
    self.Attach()
1475 2d0c8319 Iustin Pop
    if self.minor is None:
1476 2d0c8319 Iustin Pop
      # local device completely unconfigured
1477 1063abd1 Iustin Pop
      self._FastAssemble()
1478 2d0c8319 Iustin Pop
    else:
1479 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1480 2d0c8319 Iustin Pop
      # the device
1481 1063abd1 Iustin Pop
      self._SlowAssemble()
1482 2d0c8319 Iustin Pop
1483 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1484 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1485 a2cfdea2 Iustin Pop

1486 a2cfdea2 Iustin Pop
    In case of partially attached (local device matches but no network
1487 a2cfdea2 Iustin Pop
    setup), we perform the network attach. If successful, we re-test
1488 a2cfdea2 Iustin Pop
    the attach if can return success.
1489 a2cfdea2 Iustin Pop

1490 a2cfdea2 Iustin Pop
    """
1491 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1492 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1493 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1494 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1495 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1496 1063abd1 Iustin Pop
1497 a2cfdea2 Iustin Pop
      if match_l and match_r:
1498 1063abd1 Iustin Pop
        # everything matches
1499 a2cfdea2 Iustin Pop
        break
1500 1063abd1 Iustin Pop
1501 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1502 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1503 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1504 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1505 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1506 1063abd1 Iustin Pop
          break
1507 1063abd1 Iustin Pop
        else:
1508 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1509 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1510 1063abd1 Iustin Pop
1511 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1512 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1513 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1514 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1515 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1516 1063abd1 Iustin Pop
          break
1517 1063abd1 Iustin Pop
        else:
1518 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1519 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1520 bf25af3b Iustin Pop
1521 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1522 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1523 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1524 bf25af3b Iustin Pop
      # configuration
1525 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1526 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1527 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1528 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1529 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1530 9cdbe77f Iustin Pop
        # reconnect to our correct one
1531 1063abd1 Iustin Pop
        try:
1532 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1533 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1534 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1535 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1536 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1537 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1538 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1539 9cdbe77f Iustin Pop
        # None)
1540 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1541 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1542 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1543 9cdbe77f Iustin Pop
          break
1544 1063abd1 Iustin Pop
        else:
1545 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1546 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1547 9cdbe77f Iustin Pop
1548 a2cfdea2 Iustin Pop
    else:
1549 a2cfdea2 Iustin Pop
      minor = None
1550 a2cfdea2 Iustin Pop
1551 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1552 1063abd1 Iustin Pop
    if minor is None:
1553 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1554 1063abd1 Iustin Pop
                  self._aminor)
1555 a2cfdea2 Iustin Pop
1556 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1557 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1558 a2cfdea2 Iustin Pop

1559 2d0c8319 Iustin Pop
    This is run when in Assemble we detect our minor is unused.
1560 a2cfdea2 Iustin Pop

1561 a2cfdea2 Iustin Pop
    """
1562 a1578d63 Iustin Pop
    minor = self._aminor
1563 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1564 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1565 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1566 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1567 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1568 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1569 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1570 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1571 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1572 a2cfdea2 Iustin Pop
1573 a2cfdea2 Iustin Pop
  @classmethod
1574 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1575 b00b95dd Iustin Pop
    """Detach from the local device.
1576 b00b95dd Iustin Pop

1577 b00b95dd Iustin Pop
    I/Os will continue to be served from the remote device. If we
1578 b00b95dd Iustin Pop
    don't have a remote device, this operation will fail.
1579 b00b95dd Iustin Pop

1580 b00b95dd Iustin Pop
    """
1581 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1582 b00b95dd Iustin Pop
    if result.failed:
1583 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1584 b00b95dd Iustin Pop
1585 b00b95dd Iustin Pop
  @classmethod
1586 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1587 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1588 f3e513ad Iustin Pop

1589 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1590 f3e513ad Iustin Pop

1591 f3e513ad Iustin Pop
    """
1592 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1593 a8459f1c Iustin Pop
    if result.failed:
1594 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1595 f3e513ad Iustin Pop
1596 f3e513ad Iustin Pop
  @classmethod
1597 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1598 a2cfdea2 Iustin Pop
    """Deactivate the device.
1599 a2cfdea2 Iustin Pop

1600 a2cfdea2 Iustin Pop
    This will, of course, fail if the device is in use.
1601 a2cfdea2 Iustin Pop

1602 a2cfdea2 Iustin Pop
    """
1603 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1604 a2cfdea2 Iustin Pop
    if result.failed:
1605 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1606 33bc6f01 Iustin Pop
                  minor, result.output)
1607 a2cfdea2 Iustin Pop
1608 a2cfdea2 Iustin Pop
  def Shutdown(self):
1609 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1610 a2cfdea2 Iustin Pop

1611 a2cfdea2 Iustin Pop
    """
1612 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1613 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1614 746f7476 Iustin Pop
      return
1615 746f7476 Iustin Pop
    minor = self.minor
1616 a2cfdea2 Iustin Pop
    self.minor = None
1617 a2cfdea2 Iustin Pop
    self.dev_path = None
1618 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1619 a2cfdea2 Iustin Pop
1620 a2cfdea2 Iustin Pop
  def Remove(self):
1621 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1622 a2cfdea2 Iustin Pop

1623 a2cfdea2 Iustin Pop
    """
1624 0c6c04ec Iustin Pop
    self.Shutdown()
1625 a2cfdea2 Iustin Pop
1626 a2cfdea2 Iustin Pop
  @classmethod
1627 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1628 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1629 a2cfdea2 Iustin Pop

1630 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1631 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1632 a2cfdea2 Iustin Pop

1633 a2cfdea2 Iustin Pop
    """
1634 a2cfdea2 Iustin Pop
    if len(children) != 2:
1635 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1636 767d52d3 Iustin Pop
    # check that the minor is unused
1637 767d52d3 Iustin Pop
    aminor = unique_id[4]
1638 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1639 767d52d3 Iustin Pop
    if aminor in proc_info:
1640 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1641 767d52d3 Iustin Pop
      in_use = status.is_in_use
1642 767d52d3 Iustin Pop
    else:
1643 767d52d3 Iustin Pop
      in_use = False
1644 767d52d3 Iustin Pop
    if in_use:
1645 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1646 a2cfdea2 Iustin Pop
    meta = children[1]
1647 a2cfdea2 Iustin Pop
    meta.Assemble()
1648 a2cfdea2 Iustin Pop
    if not meta.Attach():
1649 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1650 33bc6f01 Iustin Pop
                  aminor, meta)
1651 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1652 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1653 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1654 a2cfdea2 Iustin Pop
1655 1005d816 Iustin Pop
  def Grow(self, amount):
1656 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1657 1005d816 Iustin Pop

1658 1005d816 Iustin Pop
    """
1659 1005d816 Iustin Pop
    if self.minor is None:
1660 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1661 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1662 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1663 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1664 38256320 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize", "-s",
1665 38256320 Iustin Pop
                           "%dm" % (self.size + amount)])
1666 1005d816 Iustin Pop
    if result.failed:
1667 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1668 1005d816 Iustin Pop
1669 a8083063 Iustin Pop
1670 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1671 6f695a2e Manuel Franceschini
  """File device.
1672 abdf0113 Iustin Pop

1673 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1674 6f695a2e Manuel Franceschini

1675 6f695a2e Manuel Franceschini
  The unique_id for the file device is a (file_driver, file_path) tuple.
1676 abdf0113 Iustin Pop

1677 6f695a2e Manuel Franceschini
  """
1678 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1679 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1680 6f695a2e Manuel Franceschini

1681 6f695a2e Manuel Franceschini
    """
1682 6f695a2e Manuel Franceschini
    if children:
1683 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1684 464f8daf Iustin Pop
    super(FileStorage, self).__init__(unique_id, children, size)
1685 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1686 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1687 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1688 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1689 ecb091e3 Iustin Pop
    self.Attach()
1690 6f695a2e Manuel Franceschini
1691 6f695a2e Manuel Franceschini
  def Assemble(self):
1692 6f695a2e Manuel Franceschini
    """Assemble the device.
1693 6f695a2e Manuel Franceschini

1694 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1695 6f695a2e Manuel Franceschini

1696 6f695a2e Manuel Franceschini
    """
1697 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1698 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1699 6f695a2e Manuel Franceschini
1700 6f695a2e Manuel Franceschini
  def Shutdown(self):
1701 6f695a2e Manuel Franceschini
    """Shutdown the device.
1702 6f695a2e Manuel Franceschini

1703 6f695a2e Manuel Franceschini
    This is a no-op for the file type, as we don't deacivate
1704 6f695a2e Manuel Franceschini
    the file on shutdown.
1705 6f695a2e Manuel Franceschini

1706 6f695a2e Manuel Franceschini
    """
1707 746f7476 Iustin Pop
    pass
1708 6f695a2e Manuel Franceschini
1709 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1710 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1711 6f695a2e Manuel Franceschini

1712 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1713 6f695a2e Manuel Franceschini

1714 6f695a2e Manuel Franceschini
    """
1715 6f695a2e Manuel Franceschini
    pass
1716 6f695a2e Manuel Franceschini
1717 6f695a2e Manuel Franceschini
  def Close(self):
1718 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1719 6f695a2e Manuel Franceschini

1720 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1721 6f695a2e Manuel Franceschini

1722 6f695a2e Manuel Franceschini
    """
1723 6f695a2e Manuel Franceschini
    pass
1724 6f695a2e Manuel Franceschini
1725 6f695a2e Manuel Franceschini
  def Remove(self):
1726 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1727 6f695a2e Manuel Franceschini

1728 c41eea6e Iustin Pop
    @rtype: boolean
1729 c41eea6e Iustin Pop
    @return: True if the removal was successful
1730 6f695a2e Manuel Franceschini

1731 6f695a2e Manuel Franceschini
    """
1732 6f695a2e Manuel Franceschini
    try:
1733 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1734 6f695a2e Manuel Franceschini
    except OSError, err:
1735 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1736 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1737 6f695a2e Manuel Franceschini
1738 6f695a2e Manuel Franceschini
  def Attach(self):
1739 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1740 6f695a2e Manuel Franceschini

1741 6f695a2e Manuel Franceschini
    Check if this file already exists.
1742 6f695a2e Manuel Franceschini

1743 c41eea6e Iustin Pop
    @rtype: boolean
1744 c41eea6e Iustin Pop
    @return: True if file exists
1745 6f695a2e Manuel Franceschini

1746 6f695a2e Manuel Franceschini
    """
1747 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1748 ecb091e3 Iustin Pop
    return self.attached
1749 6f695a2e Manuel Franceschini
1750 fcff3897 Iustin Pop
  def GetActualSize(self):
1751 fcff3897 Iustin Pop
    """Return the actual disk size.
1752 fcff3897 Iustin Pop

1753 fcff3897 Iustin Pop
    @note: the device needs to be active when this is called
1754 fcff3897 Iustin Pop

1755 fcff3897 Iustin Pop
    """
1756 fcff3897 Iustin Pop
    assert self.attached, "BlockDevice not attached in GetActualSize()"
1757 fcff3897 Iustin Pop
    try:
1758 fcff3897 Iustin Pop
      st = os.stat(self.dev_path)
1759 fcff3897 Iustin Pop
      return st.st_size
1760 fcff3897 Iustin Pop
    except OSError, err:
1761 fcff3897 Iustin Pop
      _ThrowError("Can't stat %s: %s", self.dev_path, err)
1762 fcff3897 Iustin Pop
1763 6f695a2e Manuel Franceschini
  @classmethod
1764 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1765 6f695a2e Manuel Franceschini
    """Create a new file.
1766 6f695a2e Manuel Franceschini

1767 c41eea6e Iustin Pop
    @param size: the size of file in MiB
1768 6f695a2e Manuel Franceschini

1769 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1770 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1771 6f695a2e Manuel Franceschini

1772 6f695a2e Manuel Franceschini
    """
1773 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1774 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1775 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1776 aed77cea Guido Trotter
    if os.path.exists(dev_path):
1777 aed77cea Guido Trotter
      _ThrowError("File already existing: %s", dev_path)
1778 6f695a2e Manuel Franceschini
    try:
1779 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1780 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1781 6f695a2e Manuel Franceschini
      f.close()
1782 6c626518 Iustin Pop
    except IOError, err:
1783 82463074 Iustin Pop
      _ThrowError("Error in file creation: %", str(err))
1784 6f695a2e Manuel Franceschini
1785 464f8daf Iustin Pop
    return FileStorage(unique_id, children, size)
1786 6f695a2e Manuel Franceschini
1787 6f695a2e Manuel Franceschini
1788 a8083063 Iustin Pop
DEV_MAP = {
1789 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1790 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1791 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1792 a8083063 Iustin Pop
  }
1793 a8083063 Iustin Pop
1794 a8083063 Iustin Pop
1795 464f8daf Iustin Pop
def FindDevice(dev_type, unique_id, children, size):
1796 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1797 a8083063 Iustin Pop

1798 a8083063 Iustin Pop
  This will succeed only if the device exists and is assembled, but it
1799 a8083063 Iustin Pop
  does not do any actions in order to activate the device.
1800 a8083063 Iustin Pop

1801 a8083063 Iustin Pop
  """
1802 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1803 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1804 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1805 cb999543 Iustin Pop
  if not device.attached:
1806 a8083063 Iustin Pop
    return None
1807 ecb091e3 Iustin Pop
  return device
1808 a8083063 Iustin Pop
1809 a8083063 Iustin Pop
1810 464f8daf Iustin Pop
def Assemble(dev_type, unique_id, children, size):
1811 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1812 a8083063 Iustin Pop

1813 f96e3c4f Iustin Pop
  This will attach to assemble the device, as needed, to bring it
1814 f96e3c4f Iustin Pop
  fully up. It must be safe to run on already-assembled devices.
1815 a8083063 Iustin Pop

1816 a8083063 Iustin Pop
  """
1817 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1818 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1819 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1820 1063abd1 Iustin Pop
  device.Assemble()
1821 a8083063 Iustin Pop
  return device
1822 a8083063 Iustin Pop
1823 a8083063 Iustin Pop
1824 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1825 a8083063 Iustin Pop
  """Create a device.
1826 a8083063 Iustin Pop

1827 a8083063 Iustin Pop
  """
1828 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1829 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1830 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1831 a8083063 Iustin Pop
  return device