Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 6d2e83d5

History | View | Annotate | Download (55 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 a8083063 Iustin Pop
class BlockDev(object):
37 a8083063 Iustin Pop
  """Block device abstract class.
38 a8083063 Iustin Pop

39 a8083063 Iustin Pop
  A block device can be in the following states:
40 a8083063 Iustin Pop
    - not existing on the system, and by `Create()` it goes into:
41 a8083063 Iustin Pop
    - existing but not setup/not active, and by `Assemble()` goes into:
42 a8083063 Iustin Pop
    - active read-write and by `Open()` it goes into
43 a8083063 Iustin Pop
    - online (=used, or ready for use)
44 a8083063 Iustin Pop

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

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

56 a8083063 Iustin Pop
  A block device is identified by three items:
57 a8083063 Iustin Pop
    - the /dev path of the device (dynamic)
58 a8083063 Iustin Pop
    - a unique ID of the device (static)
59 a8083063 Iustin Pop
    - it's major/minor pair (dynamic)
60 a8083063 Iustin Pop

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

67 a8083063 Iustin Pop
  You can get to a device in two ways:
68 a8083063 Iustin Pop
    - creating the (real) device, which returns you
69 abdf0113 Iustin Pop
      an attached instance (lvcreate)
70 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
71 a8083063 Iustin Pop

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

78 a8083063 Iustin Pop
  """
79 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
80 a8083063 Iustin Pop
    self._children = children
81 a8083063 Iustin Pop
    self.dev_path = None
82 a8083063 Iustin Pop
    self.unique_id = unique_id
83 a8083063 Iustin Pop
    self.major = None
84 a8083063 Iustin Pop
    self.minor = None
85 cb999543 Iustin Pop
    self.attached = False
86 a8083063 Iustin Pop
87 a8083063 Iustin Pop
  def Assemble(self):
88 a8083063 Iustin Pop
    """Assemble the device from its components.
89 a8083063 Iustin Pop

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

98 a8083063 Iustin Pop
    """
99 f87548b5 Iustin Pop
    return True
100 a8083063 Iustin Pop
101 a8083063 Iustin Pop
  def Attach(self):
102 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
103 a8083063 Iustin Pop

104 a8083063 Iustin Pop
    """
105 a8083063 Iustin Pop
    raise NotImplementedError
106 a8083063 Iustin Pop
107 a8083063 Iustin Pop
  def Close(self):
108 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
109 a8083063 Iustin Pop

110 a8083063 Iustin Pop
    """
111 a8083063 Iustin Pop
    raise NotImplementedError
112 a8083063 Iustin Pop
113 a8083063 Iustin Pop
  @classmethod
114 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
115 a8083063 Iustin Pop
    """Create the device.
116 a8083063 Iustin Pop

117 a8083063 Iustin Pop
    If the device cannot be created, it will return None
118 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
119 a8083063 Iustin Pop

120 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
121 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
122 a8083063 Iustin Pop
    enough for both creation and assembly (later).
123 a8083063 Iustin Pop

124 a8083063 Iustin Pop
    """
125 a8083063 Iustin Pop
    raise NotImplementedError
126 a8083063 Iustin Pop
127 a8083063 Iustin Pop
  def Remove(self):
128 a8083063 Iustin Pop
    """Remove this device.
129 a8083063 Iustin Pop

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

134 a8083063 Iustin Pop
    """
135 a8083063 Iustin Pop
    raise NotImplementedError
136 a8083063 Iustin Pop
137 f3e513ad Iustin Pop
  def Rename(self, new_id):
138 f3e513ad Iustin Pop
    """Rename this device.
139 f3e513ad Iustin Pop

140 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
141 f3e513ad Iustin Pop

142 f3e513ad Iustin Pop
    """
143 f3e513ad Iustin Pop
    raise NotImplementedError
144 f3e513ad Iustin Pop
145 a8083063 Iustin Pop
  def Open(self, force=False):
146 a8083063 Iustin Pop
    """Make the device ready for use.
147 a8083063 Iustin Pop

148 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
149 a8083063 Iustin Pop
    devices need this.
150 a8083063 Iustin Pop

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

154 a8083063 Iustin Pop
    """
155 a8083063 Iustin Pop
    raise NotImplementedError
156 a8083063 Iustin Pop
157 a8083063 Iustin Pop
  def Shutdown(self):
158 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
159 a8083063 Iustin Pop

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

164 a8083063 Iustin Pop
    """
165 a8083063 Iustin Pop
    raise NotImplementedError
166 a8083063 Iustin Pop
167 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
168 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
169 a8083063 Iustin Pop

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

172 a8083063 Iustin Pop
    """
173 a8083063 Iustin Pop
    result = True
174 a8083063 Iustin Pop
    if self._children:
175 a8083063 Iustin Pop
      for child in self._children:
176 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
177 a8083063 Iustin Pop
    return result
178 a8083063 Iustin Pop
179 a8083063 Iustin Pop
  def GetSyncStatus(self):
180 a8083063 Iustin Pop
    """Returns the sync status of the device.
181 a8083063 Iustin Pop

182 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
183 a8083063 Iustin Pop
    status of the mirror.
184 a8083063 Iustin Pop

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

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

190 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
191 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
192 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
193 a8083063 Iustin Pop

194 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
195 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
196 0834c866 Iustin Pop
    return False (not degraded).
197 0834c866 Iustin Pop

198 c41eea6e Iustin Pop
    @rtype: tuple
199 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
200 c41eea6e Iustin Pop

201 a8083063 Iustin Pop
    """
202 0834c866 Iustin Pop
    return None, None, False, False
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
206 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
207 a8083063 Iustin Pop

208 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
209 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
210 a8083063 Iustin Pop
    children.
211 a8083063 Iustin Pop

212 a8083063 Iustin Pop
    """
213 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
214 a8083063 Iustin Pop
    if self._children:
215 a8083063 Iustin Pop
      for child in self._children:
216 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
217 a8083063 Iustin Pop
        if min_percent is None:
218 a8083063 Iustin Pop
          min_percent = c_percent
219 a8083063 Iustin Pop
        elif c_percent is not None:
220 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
221 a8083063 Iustin Pop
        if max_time is None:
222 a8083063 Iustin Pop
          max_time = c_time
223 a8083063 Iustin Pop
        elif c_time is not None:
224 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
225 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
226 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
227 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
228 a8083063 Iustin Pop
229 a8083063 Iustin Pop
230 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
231 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
232 a0c3fea1 Michael Hanselmann

233 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
234 a0c3fea1 Michael Hanselmann

235 a0c3fea1 Michael Hanselmann
    """
236 a0c3fea1 Michael Hanselmann
    for child in self._children:
237 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
238 a0c3fea1 Michael Hanselmann
239 1005d816 Iustin Pop
  def Grow(self, amount):
240 1005d816 Iustin Pop
    """Grow the block device.
241 1005d816 Iustin Pop

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

244 1005d816 Iustin Pop
    """
245 1005d816 Iustin Pop
    raise NotImplementedError
246 a0c3fea1 Michael Hanselmann
247 a8083063 Iustin Pop
  def __repr__(self):
248 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
249 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
250 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
251 a8083063 Iustin Pop
252 a8083063 Iustin Pop
253 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
254 a8083063 Iustin Pop
  """Logical Volume block device.
255 a8083063 Iustin Pop

256 a8083063 Iustin Pop
  """
257 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
258 a8083063 Iustin Pop
    """Attaches to a LV device.
259 a8083063 Iustin Pop

260 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
261 a8083063 Iustin Pop

262 a8083063 Iustin Pop
    """
263 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
264 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
265 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
266 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
267 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
268 99e8295c Iustin Pop
    self._degraded = True
269 99e8295c Iustin Pop
    self.major = self.minor = None
270 a8083063 Iustin Pop
    self.Attach()
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
  @classmethod
273 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
274 a8083063 Iustin Pop
    """Create a new logical volume.
275 a8083063 Iustin Pop

276 a8083063 Iustin Pop
    """
277 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
278 6c626518 Iustin Pop
      raise errors.ProgrammerError("Invalid configuration data %s" %
279 6c626518 Iustin Pop
                                   str(unique_id))
280 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
281 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
282 a8083063 Iustin Pop
    if not pvs_info:
283 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
284 3ecf6786 Iustin Pop
                                    vg_name)
285 a8083063 Iustin Pop
    pvs_info.sort()
286 a8083063 Iustin Pop
    pvs_info.reverse()
287 5b7b5d49 Guido Trotter
288 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
289 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
290 5b7b5d49 Guido Trotter
291 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
292 5b7b5d49 Guido Trotter
    # calling the create function.
293 a8083063 Iustin Pop
    if free_size < size:
294 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
295 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
296 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
297 5b7b5d49 Guido Trotter
                           vg_name] + pvlist)
298 a8083063 Iustin Pop
    if result.failed:
299 6c626518 Iustin Pop
      raise errors.BlockDeviceError("LV create failed (%s): %s" %
300 6c626518 Iustin Pop
                                    (result.fail_reason, result.output))
301 a8083063 Iustin Pop
    return LogicalVolume(unique_id, children)
302 a8083063 Iustin Pop
303 a8083063 Iustin Pop
  @staticmethod
304 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
305 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
306 a8083063 Iustin Pop

307 c41eea6e Iustin Pop
    @param vg_name: the volume group name
308 a8083063 Iustin Pop

309 c41eea6e Iustin Pop
    @rtype: list
310 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
311 098c0958 Michael Hanselmann

312 a8083063 Iustin Pop
    """
313 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
314 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
315 a8083063 Iustin Pop
               "--separator=:"]
316 a8083063 Iustin Pop
    result = utils.RunCmd(command)
317 a8083063 Iustin Pop
    if result.failed:
318 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
319 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
320 a8083063 Iustin Pop
      return None
321 a8083063 Iustin Pop
    data = []
322 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
323 a8083063 Iustin Pop
      fields = line.strip().split(':')
324 a8083063 Iustin Pop
      if len(fields) != 4:
325 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
326 a8083063 Iustin Pop
        return None
327 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
328 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
329 a8083063 Iustin Pop
        continue
330 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
331 a8083063 Iustin Pop
332 a8083063 Iustin Pop
    return data
333 a8083063 Iustin Pop
334 a8083063 Iustin Pop
  def Remove(self):
335 a8083063 Iustin Pop
    """Remove this logical volume.
336 a8083063 Iustin Pop

337 a8083063 Iustin Pop
    """
338 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
339 a8083063 Iustin Pop
      # the LV does not exist
340 a8083063 Iustin Pop
      return True
341 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
342 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
343 a8083063 Iustin Pop
    if result.failed:
344 468c5f77 Iustin Pop
      logging.error("Can't lvremove: %s - %s",
345 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
346 a8083063 Iustin Pop
347 a8083063 Iustin Pop
    return not result.failed
348 a8083063 Iustin Pop
349 f3e513ad Iustin Pop
  def Rename(self, new_id):
350 f3e513ad Iustin Pop
    """Rename this logical volume.
351 f3e513ad Iustin Pop

352 f3e513ad Iustin Pop
    """
353 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
354 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
355 f3e513ad Iustin Pop
    new_vg, new_name = new_id
356 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
357 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
358 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
359 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
360 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
361 f3e513ad Iustin Pop
    if result.failed:
362 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
363 f3e513ad Iustin Pop
                                    result.output)
364 be345db0 Iustin Pop
    self._lv_name = new_name
365 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
366 be345db0 Iustin Pop
367 a8083063 Iustin Pop
  def Attach(self):
368 a8083063 Iustin Pop
    """Attach to an existing LV.
369 a8083063 Iustin Pop

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

374 a8083063 Iustin Pop
    """
375 cb999543 Iustin Pop
    self.attached = False
376 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
377 99e8295c Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor",
378 99e8295c Iustin Pop
                           self.dev_path])
379 a8083063 Iustin Pop
    if result.failed:
380 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
381 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
382 a8083063 Iustin Pop
      return False
383 99e8295c Iustin Pop
    out = result.stdout.strip().rstrip(',')
384 99e8295c Iustin Pop
    out = out.split(",")
385 99e8295c Iustin Pop
    if len(out) != 3:
386 468c5f77 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 3", str(out))
387 99e8295c Iustin Pop
      return False
388 99e8295c Iustin Pop
389 99e8295c Iustin Pop
    status, major, minor = out[:3]
390 99e8295c Iustin Pop
    if len(status) != 6:
391 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
392 99e8295c Iustin Pop
      return False
393 99e8295c Iustin Pop
394 99e8295c Iustin Pop
    try:
395 99e8295c Iustin Pop
      major = int(major)
396 99e8295c Iustin Pop
      minor = int(minor)
397 99e8295c Iustin Pop
    except ValueError, err:
398 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
399 99e8295c Iustin Pop
400 99e8295c Iustin Pop
    self.major = major
401 99e8295c Iustin Pop
    self.minor = minor
402 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
403 99e8295c Iustin Pop
                                      # storage
404 cb999543 Iustin Pop
    self.attached = True
405 99e8295c Iustin Pop
    return True
406 a8083063 Iustin Pop
407 a8083063 Iustin Pop
  def Assemble(self):
408 a8083063 Iustin Pop
    """Assemble the device.
409 a8083063 Iustin Pop

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

414 a8083063 Iustin Pop
    """
415 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
416 5574047a Iustin Pop
    if result.failed:
417 468c5f77 Iustin Pop
      logging.error("Can't activate lv %s: %s", self.dev_path, result.output)
418 cb999543 Iustin Pop
      return False
419 cb999543 Iustin Pop
    return self.Attach()
420 a8083063 Iustin Pop
421 a8083063 Iustin Pop
  def Shutdown(self):
422 a8083063 Iustin Pop
    """Shutdown the device.
423 a8083063 Iustin Pop

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

427 a8083063 Iustin Pop
    """
428 a8083063 Iustin Pop
    return True
429 a8083063 Iustin Pop
430 9db6dbce Iustin Pop
  def GetSyncStatus(self):
431 9db6dbce Iustin Pop
    """Returns the sync status of the device.
432 9db6dbce Iustin Pop

433 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
434 9db6dbce Iustin Pop
    status of the mirror.
435 9db6dbce Iustin Pop

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

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

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

449 c41eea6e Iustin Pop
    @rtype: tuple
450 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
451 c41eea6e Iustin Pop

452 9db6dbce Iustin Pop
    """
453 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
454 9db6dbce Iustin Pop
455 a8083063 Iustin Pop
  def Open(self, force=False):
456 a8083063 Iustin Pop
    """Make the device ready for I/O.
457 a8083063 Iustin Pop

458 a8083063 Iustin Pop
    This is a no-op for the LV device type.
459 a8083063 Iustin Pop

460 a8083063 Iustin Pop
    """
461 fdbd668d Iustin Pop
    pass
462 a8083063 Iustin Pop
463 a8083063 Iustin Pop
  def Close(self):
464 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
465 a8083063 Iustin Pop

466 a8083063 Iustin Pop
    This is a no-op for the LV device type.
467 a8083063 Iustin Pop

468 a8083063 Iustin Pop
    """
469 fdbd668d Iustin Pop
    pass
470 a8083063 Iustin Pop
471 a8083063 Iustin Pop
  def Snapshot(self, size):
472 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
473 a8083063 Iustin Pop

474 a8083063 Iustin Pop
    """
475 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
476 a8083063 Iustin Pop
477 a8083063 Iustin Pop
    # remove existing snapshot if found
478 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
479 a8083063 Iustin Pop
    snap.Remove()
480 a8083063 Iustin Pop
481 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
482 a8083063 Iustin Pop
    if not pvs_info:
483 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
484 3ecf6786 Iustin Pop
                                    self._vg_name)
485 a8083063 Iustin Pop
    pvs_info.sort()
486 a8083063 Iustin Pop
    pvs_info.reverse()
487 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
488 a8083063 Iustin Pop
    if free_size < size:
489 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
490 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
491 a8083063 Iustin Pop
492 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
493 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
494 a8083063 Iustin Pop
    if result.failed:
495 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s - %s" %
496 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
497 6c896e2f Iustin Pop
                                     result.output))
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
    return snap_name
500 a8083063 Iustin Pop
501 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
502 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
503 a0c3fea1 Michael Hanselmann

504 a0c3fea1 Michael Hanselmann
    """
505 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
506 a0c3fea1 Michael Hanselmann
507 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
508 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
509 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
510 a0c3fea1 Michael Hanselmann
511 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
512 a0c3fea1 Michael Hanselmann
    text = text[:128]
513 a0c3fea1 Michael Hanselmann
514 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
515 a0c3fea1 Michael Hanselmann
                           self.dev_path])
516 a0c3fea1 Michael Hanselmann
    if result.failed:
517 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s - %s" %
518 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
519 6c896e2f Iustin Pop
                                     result.output))
520 1005d816 Iustin Pop
  def Grow(self, amount):
521 1005d816 Iustin Pop
    """Grow the logical volume.
522 1005d816 Iustin Pop

523 1005d816 Iustin Pop
    """
524 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
525 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
526 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
527 1005d816 Iustin Pop
    # supports 'cling'
528 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
529 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
530 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
531 1005d816 Iustin Pop
      if not result.failed:
532 1005d816 Iustin Pop
        return
533 1005d816 Iustin Pop
    raise errors.BlockDeviceError("Can't grow LV %s: %s" %
534 1005d816 Iustin Pop
                                  (self.dev_path, result.output))
535 a0c3fea1 Michael Hanselmann
536 a0c3fea1 Michael Hanselmann
537 6b90c22e Iustin Pop
class DRBD8Status(object):
538 6b90c22e Iustin Pop
  """A DRBD status representation class.
539 6b90c22e Iustin Pop

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

542 6b90c22e Iustin Pop
  """
543 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
544 6b90c22e Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+st:([^/]+)/(\S+)"
545 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
546 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
547 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
548 6b90c22e Iustin Pop
549 6b90c22e Iustin Pop
  def __init__(self, procline):
550 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
551 767d52d3 Iustin Pop
    if u:
552 767d52d3 Iustin Pop
      self.cstatus = "Unconfigured"
553 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
554 767d52d3 Iustin Pop
    else:
555 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
556 767d52d3 Iustin Pop
      if not m:
557 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
558 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
559 767d52d3 Iustin Pop
      self.lrole = m.group(2)
560 767d52d3 Iustin Pop
      self.rrole = m.group(3)
561 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
562 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
563 767d52d3 Iustin Pop
564 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
565 6b90c22e Iustin Pop
566 6b90c22e Iustin Pop
    self.is_standalone = self.cstatus == "StandAlone"
567 6b90c22e Iustin Pop
    self.is_wfconn = self.cstatus == "WFConnection"
568 6b90c22e Iustin Pop
    self.is_connected = self.cstatus == "Connected"
569 6b90c22e Iustin Pop
    self.is_primary = self.lrole == "Primary"
570 6b90c22e Iustin Pop
    self.is_secondary = self.lrole == "Secondary"
571 6b90c22e Iustin Pop
    self.peer_primary = self.rrole == "Primary"
572 6b90c22e Iustin Pop
    self.peer_secondary = self.rrole == "Secondary"
573 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
574 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
575 6b90c22e Iustin Pop
576 6b90c22e Iustin Pop
    self.is_diskless = self.ldisk == "Diskless"
577 6b90c22e Iustin Pop
    self.is_disk_uptodate = self.ldisk == "UpToDate"
578 6b90c22e Iustin Pop
579 767d52d3 Iustin Pop
    self.is_in_resync = self.cstatus in ("SyncSource", "SyncTarget")
580 767d52d3 Iustin Pop
    self.is_in_use = self.cstatus != "Unconfigured"
581 6b93ec9d Iustin Pop
582 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
583 6b90c22e Iustin Pop
    if m:
584 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
585 6b90c22e Iustin Pop
      hours = int(m.group(2))
586 6b90c22e Iustin Pop
      minutes = int(m.group(3))
587 6b90c22e Iustin Pop
      seconds = int(m.group(4))
588 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
589 6b90c22e Iustin Pop
    else:
590 6b90c22e Iustin Pop
      self.sync_percent = None
591 6b90c22e Iustin Pop
      self.est_time = None
592 6b90c22e Iustin Pop
593 6b90c22e Iustin Pop
    self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget"
594 6b90c22e Iustin Pop
    self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource"
595 6b90c22e Iustin Pop
    self.is_resync = self.is_sync_target or self.is_sync_source
596 6b90c22e Iustin Pop
597 6b90c22e Iustin Pop
598 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
599 0f7f32d9 Iustin Pop
  """Base DRBD class.
600 a8083063 Iustin Pop

601 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
602 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
603 0f7f32d9 Iustin Pop

604 abdf0113 Iustin Pop
  """
605 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
606 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
607 a8083063 Iustin Pop
608 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
609 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
610 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
611 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
612 a8083063 Iustin Pop
613 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
614 6b90c22e Iustin Pop
615 abdf0113 Iustin Pop
  @staticmethod
616 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
617 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
618 a8083063 Iustin Pop

619 a8083063 Iustin Pop
    """
620 6b90c22e Iustin Pop
    stat = open(filename, "r")
621 abdf0113 Iustin Pop
    try:
622 abdf0113 Iustin Pop
      data = stat.read().splitlines()
623 abdf0113 Iustin Pop
    finally:
624 abdf0113 Iustin Pop
      stat.close()
625 abdf0113 Iustin Pop
    if not data:
626 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from %s" % filename)
627 abdf0113 Iustin Pop
    return data
628 a8083063 Iustin Pop
629 abdf0113 Iustin Pop
  @staticmethod
630 abdf0113 Iustin Pop
  def _MassageProcData(data):
631 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
632 a8083063 Iustin Pop

633 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
634 c41eea6e Iustin Pop
        for that minor
635 a8083063 Iustin Pop

636 a8083063 Iustin Pop
    """
637 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
638 abdf0113 Iustin Pop
    results = {}
639 abdf0113 Iustin Pop
    old_minor = old_line = None
640 abdf0113 Iustin Pop
    for line in data:
641 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
642 abdf0113 Iustin Pop
      if lresult is not None:
643 abdf0113 Iustin Pop
        if old_minor is not None:
644 abdf0113 Iustin Pop
          results[old_minor] = old_line
645 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
646 abdf0113 Iustin Pop
        old_line = line
647 abdf0113 Iustin Pop
      else:
648 abdf0113 Iustin Pop
        if old_minor is not None:
649 abdf0113 Iustin Pop
          old_line += " " + line.strip()
650 abdf0113 Iustin Pop
    # add last line
651 abdf0113 Iustin Pop
    if old_minor is not None:
652 abdf0113 Iustin Pop
      results[old_minor] = old_line
653 abdf0113 Iustin Pop
    return results
654 a8083063 Iustin Pop
655 abdf0113 Iustin Pop
  @classmethod
656 abdf0113 Iustin Pop
  def _GetVersion(cls):
657 abdf0113 Iustin Pop
    """Return the DRBD version.
658 a8083063 Iustin Pop

659 abdf0113 Iustin Pop
    This will return a dict with keys:
660 c41eea6e Iustin Pop
      - k_major
661 c41eea6e Iustin Pop
      - k_minor
662 c41eea6e Iustin Pop
      - k_point
663 c41eea6e Iustin Pop
      - api
664 c41eea6e Iustin Pop
      - proto
665 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
666 a8083063 Iustin Pop

667 a8083063 Iustin Pop
    """
668 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
669 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
670 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
671 abdf0113 Iustin Pop
    if not version:
672 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
673 abdf0113 Iustin Pop
                                    first_line)
674 a8083063 Iustin Pop
675 abdf0113 Iustin Pop
    values = version.groups()
676 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
677 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
678 abdf0113 Iustin Pop
              'k_point': int(values[2]),
679 abdf0113 Iustin Pop
              'api': int(values[3]),
680 abdf0113 Iustin Pop
              'proto': int(values[4]),
681 abdf0113 Iustin Pop
             }
682 abdf0113 Iustin Pop
    if values[5] is not None:
683 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
684 a8083063 Iustin Pop
685 abdf0113 Iustin Pop
    return retval
686 abdf0113 Iustin Pop
687 abdf0113 Iustin Pop
  @staticmethod
688 abdf0113 Iustin Pop
  def _DevPath(minor):
689 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
690 a8083063 Iustin Pop

691 a8083063 Iustin Pop
    """
692 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
693 a8083063 Iustin Pop
694 abdf0113 Iustin Pop
  @classmethod
695 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
696 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
697 a8083063 Iustin Pop

698 a8083063 Iustin Pop
    """
699 abdf0113 Iustin Pop
    data = cls._GetProcData()
700 a8083063 Iustin Pop
701 abdf0113 Iustin Pop
    used_devs = {}
702 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
703 abdf0113 Iustin Pop
    for line in data:
704 abdf0113 Iustin Pop
      match = valid_line.match(line)
705 abdf0113 Iustin Pop
      if not match:
706 abdf0113 Iustin Pop
        continue
707 abdf0113 Iustin Pop
      minor = int(match.group(1))
708 abdf0113 Iustin Pop
      state = match.group(2)
709 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
710 abdf0113 Iustin Pop
        continue
711 abdf0113 Iustin Pop
      used_devs[minor] = state, line
712 a8083063 Iustin Pop
713 abdf0113 Iustin Pop
    return used_devs
714 a8083063 Iustin Pop
715 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
716 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
717 0834c866 Iustin Pop

718 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
719 a8083063 Iustin Pop

720 a8083063 Iustin Pop
    """
721 abdf0113 Iustin Pop
    if minor is None:
722 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
723 cb999543 Iustin Pop
      self.attached = False
724 a8083063 Iustin Pop
    else:
725 abdf0113 Iustin Pop
      self.minor = minor
726 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
727 cb999543 Iustin Pop
      self.attached = True
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
  @staticmethod
730 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
731 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
732 a8083063 Iustin Pop

733 abdf0113 Iustin Pop
    This currently only check the size, which must be around
734 abdf0113 Iustin Pop
    128MiB.
735 a8083063 Iustin Pop

736 a8083063 Iustin Pop
    """
737 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
738 abdf0113 Iustin Pop
    if result.failed:
739 468c5f77 Iustin Pop
      logging.error("Failed to get device size: %s - %s",
740 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
741 abdf0113 Iustin Pop
      return False
742 a8083063 Iustin Pop
    try:
743 abdf0113 Iustin Pop
      sectors = int(result.stdout)
744 abdf0113 Iustin Pop
    except ValueError:
745 468c5f77 Iustin Pop
      logging.error("Invalid output from blockdev: '%s'", result.stdout)
746 abdf0113 Iustin Pop
      return False
747 abdf0113 Iustin Pop
    bytes = sectors * 512
748 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
749 468c5f77 Iustin Pop
      logging.error("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
750 abdf0113 Iustin Pop
      return False
751 abdf0113 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
752 468c5f77 Iustin Pop
      logging.error("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
753 abdf0113 Iustin Pop
      return False
754 abdf0113 Iustin Pop
    return True
755 a8083063 Iustin Pop
756 abdf0113 Iustin Pop
  def Rename(self, new_id):
757 abdf0113 Iustin Pop
    """Rename a device.
758 a8083063 Iustin Pop

759 abdf0113 Iustin Pop
    This is not supported for drbd devices.
760 a8083063 Iustin Pop

761 a8083063 Iustin Pop
    """
762 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
763 a8083063 Iustin Pop
764 f3e513ad Iustin Pop
765 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
766 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
767 a2cfdea2 Iustin Pop

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

772 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
773 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
774 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
775 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
776 a2cfdea2 Iustin Pop

777 a2cfdea2 Iustin Pop
  """
778 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
779 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
780 a2cfdea2 Iustin Pop
781 cf8df3f3 Iustin Pop
  # timeout constants
782 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
783 cf8df3f3 Iustin Pop
784 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
785 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
786 fc1dc9d7 Iustin Pop
      children = []
787 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
788 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
789 c3f9340c Guido Trotter
    version = self._GetVersion()
790 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
791 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
792 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
793 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
794 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
795 a2cfdea2 Iustin Pop
796 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
797 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
798 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
799 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
800 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
801 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
802 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
803 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
804 ffa1c0dc Iustin Pop
        self._lport == self._rport):
805 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
806 ffa1c0dc Iustin Pop
                       (unique_id,))
807 a2cfdea2 Iustin Pop
    self.Attach()
808 a2cfdea2 Iustin Pop
809 a2cfdea2 Iustin Pop
  @classmethod
810 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
811 a2cfdea2 Iustin Pop
    """Initialize a meta device.
812 a2cfdea2 Iustin Pop

813 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
814 a2cfdea2 Iustin Pop

815 a2cfdea2 Iustin Pop
    """
816 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
817 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
818 a2cfdea2 Iustin Pop
    if result.failed:
819 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
820 a2cfdea2 Iustin Pop
                                    result.output)
821 a2cfdea2 Iustin Pop
822 a2cfdea2 Iustin Pop
  @classmethod
823 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
824 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
825 a2cfdea2 Iustin Pop

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

829 a2cfdea2 Iustin Pop
    """
830 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
831 a2cfdea2 Iustin Pop
832 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
833 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
834 a2cfdea2 Iustin Pop
    highest = None
835 a2cfdea2 Iustin Pop
    for line in data:
836 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
837 a2cfdea2 Iustin Pop
      if match:
838 a2cfdea2 Iustin Pop
        return int(match.group(1))
839 a2cfdea2 Iustin Pop
      match = used_line.match(line)
840 a2cfdea2 Iustin Pop
      if match:
841 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
842 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
843 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
844 a2cfdea2 Iustin Pop
      return 0
845 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
846 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
847 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
848 a2cfdea2 Iustin Pop
    return highest + 1
849 a2cfdea2 Iustin Pop
850 a2cfdea2 Iustin Pop
  @classmethod
851 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
852 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
853 a2cfdea2 Iustin Pop

854 a2cfdea2 Iustin Pop
    """
855 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
856 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
857 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
858 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
859 a2cfdea2 Iustin Pop
                           "dstate"])
860 a2cfdea2 Iustin Pop
    if result.failed:
861 468c5f77 Iustin Pop
      logging.error("Invalid meta device %s: %s", meta_device, result.output)
862 a2cfdea2 Iustin Pop
      return False
863 a2cfdea2 Iustin Pop
    return True
864 a2cfdea2 Iustin Pop
865 a2cfdea2 Iustin Pop
  @classmethod
866 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
867 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
868 a2cfdea2 Iustin Pop

869 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
870 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
871 a2cfdea2 Iustin Pop

872 a2cfdea2 Iustin Pop
    """
873 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
874 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
875 a2cfdea2 Iustin Pop
876 a2cfdea2 Iustin Pop
    # pyparsing setup
877 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
878 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
879 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
880 a2cfdea2 Iustin Pop
    # this also converts the value to an int
881 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
882 a2cfdea2 Iustin Pop
883 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
884 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
885 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
886 a2cfdea2 Iustin Pop
887 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
888 a2cfdea2 Iustin Pop
889 a2cfdea2 Iustin Pop
    # value types
890 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
891 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
892 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
893 a2cfdea2 Iustin Pop
                 number)
894 a2cfdea2 Iustin Pop
    # meta device, extended syntax
895 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
896 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
897 a2cfdea2 Iustin Pop
898 a2cfdea2 Iustin Pop
    # a statement
899 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
900 63012024 Guido Trotter
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value) +
901 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
902 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
903 a2cfdea2 Iustin Pop
904 a2cfdea2 Iustin Pop
    # an entire section
905 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
906 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
907 a2cfdea2 Iustin Pop
908 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
909 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
910 a2cfdea2 Iustin Pop
911 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
912 a2cfdea2 Iustin Pop
913 a2cfdea2 Iustin Pop
    return bnf
914 a2cfdea2 Iustin Pop
915 a2cfdea2 Iustin Pop
  @classmethod
916 3840729d Iustin Pop
  def _GetShowData(cls, minor):
917 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
918 a2cfdea2 Iustin Pop

919 a2cfdea2 Iustin Pop
    """
920 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
921 a2cfdea2 Iustin Pop
    if result.failed:
922 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
923 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
924 3840729d Iustin Pop
      return None
925 3840729d Iustin Pop
    return result.stdout
926 3840729d Iustin Pop
927 3840729d Iustin Pop
  @classmethod
928 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
929 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
930 3840729d Iustin Pop

931 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
932 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
933 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
934 3840729d Iustin Pop
    by _GetShowData.
935 3840729d Iustin Pop

936 3840729d Iustin Pop
    """
937 3840729d Iustin Pop
    data = {}
938 a2cfdea2 Iustin Pop
    if not out:
939 a2cfdea2 Iustin Pop
      return data
940 a2cfdea2 Iustin Pop
941 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
942 a2cfdea2 Iustin Pop
    # run pyparse
943 a2cfdea2 Iustin Pop
944 a2cfdea2 Iustin Pop
    try:
945 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
946 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
947 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
948 a2cfdea2 Iustin Pop
                                    str(err))
949 a2cfdea2 Iustin Pop
950 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
951 a2cfdea2 Iustin Pop
    for section in results:
952 a2cfdea2 Iustin Pop
      sname = section[0]
953 a2cfdea2 Iustin Pop
      if sname == "_this_host":
954 a2cfdea2 Iustin Pop
        for lst in section[1:]:
955 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
956 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
957 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
958 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
959 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
960 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
961 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
962 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
963 a2cfdea2 Iustin Pop
        for lst in section[1:]:
964 a2cfdea2 Iustin Pop
          if lst[0] == "address":
965 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
966 a2cfdea2 Iustin Pop
    return data
967 a2cfdea2 Iustin Pop
968 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
969 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
970 a2cfdea2 Iustin Pop

971 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
972 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
973 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
974 a2cfdea2 Iustin Pop
    device.
975 a2cfdea2 Iustin Pop

976 a2cfdea2 Iustin Pop
    """
977 b00b95dd Iustin Pop
    if self._children:
978 b00b95dd Iustin Pop
      backend, meta = self._children
979 b00b95dd Iustin Pop
    else:
980 b00b95dd Iustin Pop
      backend = meta = None
981 b00b95dd Iustin Pop
982 a2cfdea2 Iustin Pop
    if backend is not None:
983 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
984 a2cfdea2 Iustin Pop
    else:
985 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
986 b00b95dd Iustin Pop
987 a2cfdea2 Iustin Pop
    if meta is not None:
988 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
989 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
990 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
991 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
992 a2cfdea2 Iustin Pop
    else:
993 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
994 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
995 a2cfdea2 Iustin Pop
    return retval
996 a2cfdea2 Iustin Pop
997 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
998 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
999 a2cfdea2 Iustin Pop

1000 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1001 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1002 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1003 a2cfdea2 Iustin Pop
    device.
1004 a2cfdea2 Iustin Pop

1005 a2cfdea2 Iustin Pop
    """
1006 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1007 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1008 a2cfdea2 Iustin Pop
      return True
1009 a2cfdea2 Iustin Pop
1010 a2cfdea2 Iustin Pop
    if self._lhost is None:
1011 a2cfdea2 Iustin Pop
      return False
1012 a2cfdea2 Iustin Pop
1013 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1014 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1015 a2cfdea2 Iustin Pop
      return False
1016 a2cfdea2 Iustin Pop
1017 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1018 a2cfdea2 Iustin Pop
    retval = (retval and
1019 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1020 a2cfdea2 Iustin Pop
    return retval
1021 a2cfdea2 Iustin Pop
1022 a2cfdea2 Iustin Pop
  @classmethod
1023 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1024 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1025 a2cfdea2 Iustin Pop

1026 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1027 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1028 a2cfdea2 Iustin Pop

1029 a2cfdea2 Iustin Pop
    """
1030 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1031 a2cfdea2 Iustin Pop
      return False
1032 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1033 333411a7 Guido Trotter
            backend, meta, "0", "-e", "detach", "--create-device"]
1034 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1035 a2cfdea2 Iustin Pop
    if result.failed:
1036 468c5f77 Iustin Pop
      logging.error("Can't attach local disk: %s", result.output)
1037 a2cfdea2 Iustin Pop
    return not result.failed
1038 a2cfdea2 Iustin Pop
1039 a2cfdea2 Iustin Pop
  @classmethod
1040 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1041 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1042 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1043 a2cfdea2 Iustin Pop

1044 a2cfdea2 Iustin Pop
    """
1045 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1046 52857176 Iustin Pop
    if None in net_info:
1047 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1048 52857176 Iustin Pop
      # sure its shutdown
1049 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1050 52857176 Iustin Pop
1051 7d585316 Iustin Pop
    # Workaround for a race condition. When DRBD is doing its dance to
1052 7d585316 Iustin Pop
    # establish a connection with its peer, it also sends the
1053 7d585316 Iustin Pop
    # synchronization speed over the wire. In some cases setting the
1054 7d585316 Iustin Pop
    # sync speed only after setting up both sides can race with DRBD
1055 7d585316 Iustin Pop
    # connecting, hence we set it here before telling DRBD anything
1056 7d585316 Iustin Pop
    # about its peer.
1057 7d585316 Iustin Pop
    cls._SetMinorSyncSpeed(minor, constants.SYNC_SPEED)
1058 7d585316 Iustin Pop
1059 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1060 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1061 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1062 f38478b2 Iustin Pop
            "-B", "consensus",
1063 ab6cc81c Iustin Pop
            "--create-device",
1064 f38478b2 Iustin Pop
            ]
1065 a2cfdea2 Iustin Pop
    if dual_pri:
1066 a2cfdea2 Iustin Pop
      args.append("-m")
1067 a2cfdea2 Iustin Pop
    if hmac and secret:
1068 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1069 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1070 a2cfdea2 Iustin Pop
    if result.failed:
1071 468c5f77 Iustin Pop
      logging.error("Can't setup network for dbrd device: %s - %s",
1072 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1073 a2cfdea2 Iustin Pop
      return False
1074 a2cfdea2 Iustin Pop
1075 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1076 a2cfdea2 Iustin Pop
    ok = False
1077 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1078 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1079 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1080 a2cfdea2 Iustin Pop
        time.sleep(1)
1081 a2cfdea2 Iustin Pop
        continue
1082 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1083 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1084 a2cfdea2 Iustin Pop
        time.sleep(1)
1085 a2cfdea2 Iustin Pop
        continue
1086 a2cfdea2 Iustin Pop
      ok = True
1087 a2cfdea2 Iustin Pop
      break
1088 a2cfdea2 Iustin Pop
    if not ok:
1089 468c5f77 Iustin Pop
      logging.error("Timeout while configuring network")
1090 a2cfdea2 Iustin Pop
      return False
1091 a2cfdea2 Iustin Pop
    return True
1092 a2cfdea2 Iustin Pop
1093 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1094 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1095 b00b95dd Iustin Pop

1096 b00b95dd Iustin Pop
    """
1097 b00b95dd Iustin Pop
    if self.minor is None:
1098 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1099 b00b95dd Iustin Pop
    if len(devices) != 2:
1100 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1101 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1102 03ece5f3 Iustin Pop
    if "local_dev" in info:
1103 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1104 b00b95dd Iustin Pop
    backend, meta = devices
1105 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1106 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1107 b00b95dd Iustin Pop
    backend.Open()
1108 b00b95dd Iustin Pop
    meta.Open()
1109 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1110 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1111 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1112 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1113 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1114 b00b95dd Iustin Pop
1115 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1116 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1117 b00b95dd Iustin Pop
    self._children = devices
1118 b00b95dd Iustin Pop
1119 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1120 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1121 b00b95dd Iustin Pop

1122 b00b95dd Iustin Pop
    """
1123 b00b95dd Iustin Pop
    if self.minor is None:
1124 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1125 b00b95dd Iustin Pop
                                    " RemoveChildren")
1126 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1127 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1128 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1129 03ece5f3 Iustin Pop
      return
1130 b00b95dd Iustin Pop
    if len(self._children) != 2:
1131 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1132 b00b95dd Iustin Pop
                                    self._children)
1133 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1134 468c5f77 Iustin Pop
      logging.error("Requested detach while detached")
1135 e739bd57 Iustin Pop
      return
1136 b00b95dd Iustin Pop
    if len(devices) != 2:
1137 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1138 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1139 e739bd57 Iustin Pop
      if dev != child.dev_path:
1140 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1141 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1142 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1143 b00b95dd Iustin Pop
1144 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1145 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1146 b00b95dd Iustin Pop
    self._children = []
1147 b00b95dd Iustin Pop
1148 7d585316 Iustin Pop
  @classmethod
1149 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1150 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1151 a2cfdea2 Iustin Pop

1152 7d585316 Iustin Pop
    This is the low-level implementation.
1153 7d585316 Iustin Pop

1154 7d585316 Iustin Pop
    @type minor: int
1155 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1156 7d585316 Iustin Pop
    @type kbytes: int
1157 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1158 7d585316 Iustin Pop
    @rtype: boolean
1159 7d585316 Iustin Pop
    @return: the success of the operation
1160 7d585316 Iustin Pop

1161 a2cfdea2 Iustin Pop
    """
1162 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1163 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1164 a2cfdea2 Iustin Pop
    if result.failed:
1165 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1166 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1167 7d585316 Iustin Pop
    return not result.failed
1168 7d585316 Iustin Pop
1169 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1170 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1171 7d585316 Iustin Pop

1172 7d585316 Iustin Pop
    @type kbytes: int
1173 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1174 7d585316 Iustin Pop
    @rtype: boolean
1175 7d585316 Iustin Pop
    @return: the success of the operation
1176 7d585316 Iustin Pop

1177 7d585316 Iustin Pop
    """
1178 7d585316 Iustin Pop
    if self.minor is None:
1179 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1180 7d585316 Iustin Pop
      return False
1181 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1182 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1183 a2cfdea2 Iustin Pop
1184 6b90c22e Iustin Pop
  def GetProcStatus(self):
1185 6b90c22e Iustin Pop
    """Return device data from /proc.
1186 6b90c22e Iustin Pop

1187 6b90c22e Iustin Pop
    """
1188 6b90c22e Iustin Pop
    if self.minor is None:
1189 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("GetStats() called while not attached")
1190 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1191 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1192 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1193 6b90c22e Iustin Pop
                                    self.minor)
1194 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1195 6b90c22e Iustin Pop
1196 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1197 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1198 a2cfdea2 Iustin Pop

1199 a2cfdea2 Iustin Pop

1200 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1201 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1202 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1203 0834c866 Iustin Pop

1204 0834c866 Iustin Pop

1205 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1206 0834c866 Iustin Pop
    network not connected or local disk missing.
1207 0834c866 Iustin Pop

1208 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1209 0834c866 Iustin Pop
    disk or not.
1210 a2cfdea2 Iustin Pop

1211 c41eea6e Iustin Pop
    @rtype: tuple
1212 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
1213 c41eea6e Iustin Pop

1214 a2cfdea2 Iustin Pop
    """
1215 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1216 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1217 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1218 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1219 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1220 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1221 a2cfdea2 Iustin Pop
1222 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1223 a2cfdea2 Iustin Pop
    """Make the local state primary.
1224 a2cfdea2 Iustin Pop

1225 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1226 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1227 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1228 f860ff4e Guido Trotter
    is mandatory.
1229 a2cfdea2 Iustin Pop

1230 a2cfdea2 Iustin Pop
    """
1231 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1232 468c5f77 Iustin Pop
      logging.error("DRBD cannot attach to a device during open")
1233 a2cfdea2 Iustin Pop
      return False
1234 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1235 a2cfdea2 Iustin Pop
    if force:
1236 a2cfdea2 Iustin Pop
      cmd.append("-o")
1237 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1238 a2cfdea2 Iustin Pop
    if result.failed:
1239 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1240 468c5f77 Iustin Pop
      logging.error(msg)
1241 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1242 a2cfdea2 Iustin Pop
1243 a2cfdea2 Iustin Pop
  def Close(self):
1244 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1245 a2cfdea2 Iustin Pop

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

1248 a2cfdea2 Iustin Pop
    """
1249 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1250 468c5f77 Iustin Pop
      logging.info("Instance not attached to a device")
1251 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1252 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1253 a2cfdea2 Iustin Pop
    if result.failed:
1254 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1255 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1256 468c5f77 Iustin Pop
      logging.error(msg)
1257 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1258 a2cfdea2 Iustin Pop
1259 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1260 cf8df3f3 Iustin Pop
    """Removes network configuration.
1261 cf8df3f3 Iustin Pop

1262 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1263 cf8df3f3 Iustin Pop

1264 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1265 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1266 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1267 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1268 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1269 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1270 cf8df3f3 Iustin Pop
    exception.
1271 cf8df3f3 Iustin Pop

1272 cf8df3f3 Iustin Pop
    """
1273 cf8df3f3 Iustin Pop
    if self.minor is None:
1274 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk not attached in re-attach net")
1275 cf8df3f3 Iustin Pop
1276 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1277 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk missing network info in"
1278 cf8df3f3 Iustin Pop
                                    " DisconnectNet()")
1279 cf8df3f3 Iustin Pop
1280 cf8df3f3 Iustin Pop
    ever_disconnected = self._ShutdownNet(self.minor)
1281 cf8df3f3 Iustin Pop
    timeout_limit = time.time() + self._NET_RECONFIG_TIMEOUT
1282 cf8df3f3 Iustin Pop
    sleep_time = 0.100 # we start the retry time at 100 miliseconds
1283 cf8df3f3 Iustin Pop
    while time.time() < timeout_limit:
1284 cf8df3f3 Iustin Pop
      status = self.GetProcStatus()
1285 cf8df3f3 Iustin Pop
      if status.is_standalone:
1286 cf8df3f3 Iustin Pop
        break
1287 cf8df3f3 Iustin Pop
      # retry the disconnect, it seems possible that due to a
1288 cf8df3f3 Iustin Pop
      # well-time disconnect on the peer, my disconnect command might
1289 cf8df3f3 Iustin Pop
      # be ingored and forgotten
1290 cf8df3f3 Iustin Pop
      ever_disconnected = self._ShutdownNet(self.minor) or ever_disconnected
1291 cf8df3f3 Iustin Pop
      time.sleep(sleep_time)
1292 cf8df3f3 Iustin Pop
      sleep_time = min(2, sleep_time * 1.5)
1293 cf8df3f3 Iustin Pop
1294 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1295 cf8df3f3 Iustin Pop
      if ever_disconnected:
1296 cf8df3f3 Iustin Pop
        msg = ("Device did not react to the"
1297 cf8df3f3 Iustin Pop
               " 'disconnect' command in a timely manner")
1298 cf8df3f3 Iustin Pop
      else:
1299 cf8df3f3 Iustin Pop
        msg = ("Can't shutdown network, even after multiple retries")
1300 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError(msg)
1301 cf8df3f3 Iustin Pop
1302 cf8df3f3 Iustin Pop
    reconfig_time = time.time() - timeout_limit + self._NET_RECONFIG_TIMEOUT
1303 cf8df3f3 Iustin Pop
    if reconfig_time > 15: # hardcoded alert limit
1304 cf8df3f3 Iustin Pop
      logging.debug("DRBD8.DisconnectNet: detach took %.3f seconds",
1305 cf8df3f3 Iustin Pop
                    reconfig_time)
1306 cf8df3f3 Iustin Pop
1307 cf8df3f3 Iustin Pop
  def AttachNet(self, multimaster):
1308 cf8df3f3 Iustin Pop
    """Reconnects the network.
1309 cf8df3f3 Iustin Pop

1310 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1311 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1312 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1313 cf8df3f3 Iustin Pop

1314 cf8df3f3 Iustin Pop
    Args:
1315 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1316 cf8df3f3 Iustin Pop

1317 cf8df3f3 Iustin Pop
    """
1318 cf8df3f3 Iustin Pop
    if self.minor is None:
1319 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk not attached in AttachNet")
1320 cf8df3f3 Iustin Pop
1321 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1322 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk missing network info in"
1323 cf8df3f3 Iustin Pop
                                    " AttachNet()")
1324 cf8df3f3 Iustin Pop
1325 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1326 cf8df3f3 Iustin Pop
1327 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1328 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("Device is not standalone in AttachNet")
1329 cf8df3f3 Iustin Pop
1330 cf8df3f3 Iustin Pop
    return self._AssembleNet(self.minor,
1331 cf8df3f3 Iustin Pop
                             (self._lhost, self._lport,
1332 cf8df3f3 Iustin Pop
                              self._rhost, self._rport),
1333 cf8df3f3 Iustin Pop
                             "C", dual_pri=multimaster)
1334 cf8df3f3 Iustin Pop
1335 a2cfdea2 Iustin Pop
  def Attach(self):
1336 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1337 2d0c8319 Iustin Pop

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

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

1345 2d0c8319 Iustin Pop
    """
1346 6d2e83d5 Iustin Pop
    used_devs = self.GetUsedDevs()
1347 2d0c8319 Iustin Pop
    if self._aminor in used_devs:
1348 2d0c8319 Iustin Pop
      minor = self._aminor
1349 2d0c8319 Iustin Pop
    else:
1350 2d0c8319 Iustin Pop
      minor = None
1351 2d0c8319 Iustin Pop
1352 2d0c8319 Iustin Pop
    self._SetFromMinor(minor)
1353 2d0c8319 Iustin Pop
    return minor is not None
1354 2d0c8319 Iustin Pop
1355 2d0c8319 Iustin Pop
  def Assemble(self):
1356 2d0c8319 Iustin Pop
    """Assemble the drbd.
1357 2d0c8319 Iustin Pop

1358 2d0c8319 Iustin Pop
    Method:
1359 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1360 2d0c8319 Iustin Pop
        our config
1361 2d0c8319 Iustin Pop
      - if not, we create it from zero
1362 2d0c8319 Iustin Pop

1363 2d0c8319 Iustin Pop
    """
1364 2d0c8319 Iustin Pop
    result = super(DRBD8, self).Assemble()
1365 2d0c8319 Iustin Pop
    if not result:
1366 2d0c8319 Iustin Pop
      return result
1367 2d0c8319 Iustin Pop
1368 2d0c8319 Iustin Pop
    self.Attach()
1369 2d0c8319 Iustin Pop
    if self.minor is None:
1370 2d0c8319 Iustin Pop
      # local device completely unconfigured
1371 2d0c8319 Iustin Pop
      return self._FastAssemble()
1372 2d0c8319 Iustin Pop
    else:
1373 2d0c8319 Iustin Pop
      # we have to recheck the local and network status and try to fix
1374 2d0c8319 Iustin Pop
      # the device
1375 2d0c8319 Iustin Pop
      return self._SlowAssemble()
1376 2d0c8319 Iustin Pop
1377 2d0c8319 Iustin Pop
  def _SlowAssemble(self):
1378 2d0c8319 Iustin Pop
    """Assembles the DRBD device from a (partially) configured device.
1379 a2cfdea2 Iustin Pop

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

1384 a2cfdea2 Iustin Pop
    """
1385 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1386 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1387 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1388 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1389 a2cfdea2 Iustin Pop
      if match_l and match_r:
1390 a2cfdea2 Iustin Pop
        break
1391 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1392 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
1393 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
1394 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
1395 3c03759a Iustin Pop
                                  constants.DRBD_NET_PROTOCOL,
1396 3c03759a Iustin Pop
                                  hmac=constants.DRBD_HMAC_ALG,
1397 2899d9de Iustin Pop
                                  secret=self._secret
1398 2899d9de Iustin Pop
                                  )
1399 3840729d Iustin Pop
        if res_r:
1400 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1401 3840729d Iustin Pop
            break
1402 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
1403 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
1404 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1405 fc1dc9d7 Iustin Pop
        break
1406 bf25af3b Iustin Pop
1407 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1408 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1409 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1410 bf25af3b Iustin Pop
      # configuration
1411 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1412 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1413 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1414 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1415 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1416 9cdbe77f Iustin Pop
        # reconnect to our correct one
1417 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
1418 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
1419 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
1420 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
1421 9cdbe77f Iustin Pop
                                        " the correct peer")
1422 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1423 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1424 9cdbe77f Iustin Pop
        # None)
1425 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
1426 3c03759a Iustin Pop
                                      self._rhost, self._rport),
1427 3c03759a Iustin Pop
                              constants.DRBD_NET_PROTOCOL,
1428 2899d9de Iustin Pop
                              hmac=constants.DRBD_HMAC_ALG,
1429 2899d9de Iustin Pop
                              secret=self._secret) and
1430 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
1431 9cdbe77f Iustin Pop
          break
1432 9cdbe77f Iustin Pop
1433 a2cfdea2 Iustin Pop
    else:
1434 a2cfdea2 Iustin Pop
      minor = None
1435 a2cfdea2 Iustin Pop
1436 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1437 a2cfdea2 Iustin Pop
    return minor is not None
1438 a2cfdea2 Iustin Pop
1439 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1440 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1441 a2cfdea2 Iustin Pop

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

1444 a2cfdea2 Iustin Pop
    """
1445 a1578d63 Iustin Pop
    # TODO: maybe completely tear-down the minor (drbdsetup ... down)
1446 a1578d63 Iustin Pop
    # before attaching our own?
1447 a1578d63 Iustin Pop
    minor = self._aminor
1448 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1449 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1450 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1451 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1452 a2cfdea2 Iustin Pop
      if not result:
1453 a2cfdea2 Iustin Pop
        return False
1454 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
1455 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1456 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1457 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1458 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1459 3c03759a Iustin Pop
                                 constants.DRBD_NET_PROTOCOL,
1460 3c03759a Iustin Pop
                                 hmac=constants.DRBD_HMAC_ALG,
1461 2899d9de Iustin Pop
                                 secret=self._secret)
1462 a2cfdea2 Iustin Pop
      if not result:
1463 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
1464 a2cfdea2 Iustin Pop
          # we will ignore failures from this
1465 468c5f77 Iustin Pop
          logging.error("net setup failed, tearing down local device")
1466 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
1467 a2cfdea2 Iustin Pop
        return False
1468 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1469 a2cfdea2 Iustin Pop
    return True
1470 a2cfdea2 Iustin Pop
1471 a2cfdea2 Iustin Pop
  @classmethod
1472 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1473 b00b95dd Iustin Pop
    """Detach from the local device.
1474 b00b95dd Iustin Pop

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

1478 b00b95dd Iustin Pop
    """
1479 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1480 b00b95dd Iustin Pop
    if result.failed:
1481 468c5f77 Iustin Pop
      logging.error("Can't detach local device: %s", result.output)
1482 b00b95dd Iustin Pop
    return not result.failed
1483 b00b95dd Iustin Pop
1484 b00b95dd Iustin Pop
  @classmethod
1485 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1486 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1487 f3e513ad Iustin Pop

1488 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1489 f3e513ad Iustin Pop

1490 f3e513ad Iustin Pop
    """
1491 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1492 a8459f1c Iustin Pop
    if result.failed:
1493 468c5f77 Iustin Pop
      logging.error("Can't shutdown network: %s", result.output)
1494 f3e513ad Iustin Pop
    return not result.failed
1495 f3e513ad Iustin Pop
1496 f3e513ad Iustin Pop
  @classmethod
1497 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1498 a2cfdea2 Iustin Pop
    """Deactivate the device.
1499 a2cfdea2 Iustin Pop

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

1502 a2cfdea2 Iustin Pop
    """
1503 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1504 a2cfdea2 Iustin Pop
    if result.failed:
1505 468c5f77 Iustin Pop
      logging.error("Can't shutdown drbd device: %s", result.output)
1506 a2cfdea2 Iustin Pop
    return not result.failed
1507 a2cfdea2 Iustin Pop
1508 a2cfdea2 Iustin Pop
  def Shutdown(self):
1509 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1510 a2cfdea2 Iustin Pop

1511 a2cfdea2 Iustin Pop
    """
1512 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1513 468c5f77 Iustin Pop
      logging.info("DRBD device not attached to a device during Shutdown")
1514 a2cfdea2 Iustin Pop
      return True
1515 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
1516 a2cfdea2 Iustin Pop
      return False
1517 a2cfdea2 Iustin Pop
    self.minor = None
1518 a2cfdea2 Iustin Pop
    self.dev_path = None
1519 a2cfdea2 Iustin Pop
    return True
1520 a2cfdea2 Iustin Pop
1521 a2cfdea2 Iustin Pop
  def Remove(self):
1522 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1523 a2cfdea2 Iustin Pop

1524 a2cfdea2 Iustin Pop
    """
1525 a2cfdea2 Iustin Pop
    return self.Shutdown()
1526 a2cfdea2 Iustin Pop
1527 a2cfdea2 Iustin Pop
  @classmethod
1528 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1529 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1530 a2cfdea2 Iustin Pop

1531 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1532 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1533 a2cfdea2 Iustin Pop

1534 a2cfdea2 Iustin Pop
    """
1535 a2cfdea2 Iustin Pop
    if len(children) != 2:
1536 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1537 767d52d3 Iustin Pop
    # check that the minor is unused
1538 767d52d3 Iustin Pop
    aminor = unique_id[4]
1539 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1540 767d52d3 Iustin Pop
    if aminor in proc_info:
1541 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1542 767d52d3 Iustin Pop
      in_use = status.is_in_use
1543 767d52d3 Iustin Pop
    else:
1544 767d52d3 Iustin Pop
      in_use = False
1545 767d52d3 Iustin Pop
    if in_use:
1546 767d52d3 Iustin Pop
      raise errors.BlockDeviceError("DRBD minor %d already in use at"
1547 767d52d3 Iustin Pop
                                    " Create() time" % aminor)
1548 a2cfdea2 Iustin Pop
    meta = children[1]
1549 a2cfdea2 Iustin Pop
    meta.Assemble()
1550 a2cfdea2 Iustin Pop
    if not meta.Attach():
1551 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1552 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1553 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1554 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
1555 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1556 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1557 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1558 a2cfdea2 Iustin Pop
1559 1005d816 Iustin Pop
  def Grow(self, amount):
1560 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1561 1005d816 Iustin Pop

1562 1005d816 Iustin Pop
    """
1563 1005d816 Iustin Pop
    if self.minor is None:
1564 1005d816 Iustin Pop
      raise errors.ProgrammerError("drbd8: Grow called while not attached")
1565 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1566 1005d816 Iustin Pop
      raise errors.BlockDeviceError("Cannot grow diskless DRBD8 device")
1567 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1568 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1569 1005d816 Iustin Pop
    if result.failed:
1570 1005d816 Iustin Pop
      raise errors.BlockDeviceError("resize failed for %s: %s" %
1571 1005d816 Iustin Pop
                                    (self.dev_path, result.output))
1572 1005d816 Iustin Pop
    return
1573 1005d816 Iustin Pop
1574 a8083063 Iustin Pop
1575 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1576 6f695a2e Manuel Franceschini
  """File device.
1577 abdf0113 Iustin Pop

1578 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1579 6f695a2e Manuel Franceschini

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

1582 6f695a2e Manuel Franceschini
  """
1583 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1584 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1585 6f695a2e Manuel Franceschini

1586 6f695a2e Manuel Franceschini
    """
1587 6f695a2e Manuel Franceschini
    if children:
1588 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1589 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
1590 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1591 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1592 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1593 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1594 ecb091e3 Iustin Pop
    self.Attach()
1595 6f695a2e Manuel Franceschini
1596 6f695a2e Manuel Franceschini
  def Assemble(self):
1597 6f695a2e Manuel Franceschini
    """Assemble the device.
1598 6f695a2e Manuel Franceschini

1599 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1600 6f695a2e Manuel Franceschini

1601 6f695a2e Manuel Franceschini
    """
1602 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1603 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1604 6f695a2e Manuel Franceschini
                                    self.dev_path)
1605 6f695a2e Manuel Franceschini
    return True
1606 6f695a2e Manuel Franceschini
1607 6f695a2e Manuel Franceschini
  def Shutdown(self):
1608 6f695a2e Manuel Franceschini
    """Shutdown the device.
1609 6f695a2e Manuel Franceschini

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

1613 6f695a2e Manuel Franceschini
    """
1614 6f695a2e Manuel Franceschini
    return True
1615 6f695a2e Manuel Franceschini
1616 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1617 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1618 6f695a2e Manuel Franceschini

1619 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1620 6f695a2e Manuel Franceschini

1621 6f695a2e Manuel Franceschini
    """
1622 6f695a2e Manuel Franceschini
    pass
1623 6f695a2e Manuel Franceschini
1624 6f695a2e Manuel Franceschini
  def Close(self):
1625 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1626 6f695a2e Manuel Franceschini

1627 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1628 6f695a2e Manuel Franceschini

1629 6f695a2e Manuel Franceschini
    """
1630 6f695a2e Manuel Franceschini
    pass
1631 6f695a2e Manuel Franceschini
1632 6f695a2e Manuel Franceschini
  def Remove(self):
1633 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1634 6f695a2e Manuel Franceschini

1635 c41eea6e Iustin Pop
    @rtype: boolean
1636 c41eea6e Iustin Pop
    @return: True if the removal was successful
1637 6f695a2e Manuel Franceschini

1638 6f695a2e Manuel Franceschini
    """
1639 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1640 6f695a2e Manuel Franceschini
      return True
1641 6f695a2e Manuel Franceschini
    try:
1642 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1643 6f695a2e Manuel Franceschini
      return True
1644 6f695a2e Manuel Franceschini
    except OSError, err:
1645 468c5f77 Iustin Pop
      logging.error("Can't remove file '%s': %s", self.dev_path, err)
1646 6f695a2e Manuel Franceschini
      return False
1647 6f695a2e Manuel Franceschini
1648 6f695a2e Manuel Franceschini
  def Attach(self):
1649 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1650 6f695a2e Manuel Franceschini

1651 6f695a2e Manuel Franceschini
    Check if this file already exists.
1652 6f695a2e Manuel Franceschini

1653 c41eea6e Iustin Pop
    @rtype: boolean
1654 c41eea6e Iustin Pop
    @return: True if file exists
1655 6f695a2e Manuel Franceschini

1656 6f695a2e Manuel Franceschini
    """
1657 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1658 ecb091e3 Iustin Pop
    return self.attached
1659 6f695a2e Manuel Franceschini
1660 6f695a2e Manuel Franceschini
  @classmethod
1661 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1662 6f695a2e Manuel Franceschini
    """Create a new file.
1663 6f695a2e Manuel Franceschini

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

1666 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1667 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1668 6f695a2e Manuel Franceschini

1669 6f695a2e Manuel Franceschini
    """
1670 6c626518 Iustin Pop
    # TODO: decide whether we should check for existing files and
1671 6c626518 Iustin Pop
    # abort or not
1672 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1673 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1674 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1675 6f695a2e Manuel Franceschini
    try:
1676 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1677 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1678 6f695a2e Manuel Franceschini
      f.close()
1679 6c626518 Iustin Pop
    except IOError, err:
1680 6c626518 Iustin Pop
      raise errors.BlockDeviceError("Error in file creation: %" % str(err))
1681 6f695a2e Manuel Franceschini
1682 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
1683 6f695a2e Manuel Franceschini
1684 6f695a2e Manuel Franceschini
1685 a8083063 Iustin Pop
DEV_MAP = {
1686 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1687 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1688 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1689 a8083063 Iustin Pop
  }
1690 a8083063 Iustin Pop
1691 a8083063 Iustin Pop
1692 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1693 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1694 a8083063 Iustin Pop

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

1698 a8083063 Iustin Pop
  """
1699 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1700 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1701 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1702 cb999543 Iustin Pop
  if not device.attached:
1703 a8083063 Iustin Pop
    return None
1704 ecb091e3 Iustin Pop
  return device
1705 a8083063 Iustin Pop
1706 a8083063 Iustin Pop
1707 f96e3c4f Iustin Pop
def Assemble(dev_type, unique_id, children):
1708 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1709 a8083063 Iustin Pop

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

1713 a8083063 Iustin Pop
  """
1714 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1715 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1716 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1717 f96e3c4f Iustin Pop
  if not device.Assemble():
1718 f96e3c4f Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
1719 f96e3c4f Iustin Pop
                                  " %s/%s/%s" %
1720 f96e3c4f Iustin Pop
                                  (dev_type, unique_id, children))
1721 a8083063 Iustin Pop
  return device
1722 a8083063 Iustin Pop
1723 a8083063 Iustin Pop
1724 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1725 a8083063 Iustin Pop
  """Create a device.
1726 a8083063 Iustin Pop

1727 a8083063 Iustin Pop
  """
1728 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1729 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1730 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1731 a8083063 Iustin Pop
  return device