Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ d0b3526f

History | View | Annotate | Download (43.5 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
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 a8083063 Iustin Pop
28 a8083063 Iustin Pop
from ganeti import utils
29 a8083063 Iustin Pop
from ganeti import logger
30 a8083063 Iustin Pop
from ganeti import errors
31 a8083063 Iustin Pop
32 a8083063 Iustin Pop
33 a8083063 Iustin Pop
class BlockDev(object):
34 a8083063 Iustin Pop
  """Block device abstract class.
35 a8083063 Iustin Pop

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

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

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

54 a8083063 Iustin Pop
  The status of the device can be examined by `GetStatus()`, which
55 a8083063 Iustin Pop
  returns a numerical value, depending on the position in the
56 a8083063 Iustin Pop
  transition stack of the device.
57 a8083063 Iustin Pop

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

63 a8083063 Iustin Pop
  Not all devices implement both the first two as distinct items. LVM
64 a8083063 Iustin Pop
  logical volumes have their unique ID (the pair volume group, logical
65 a8083063 Iustin Pop
  volume name) in a 1-to-1 relation to the dev path. For MD devices,
66 a8083063 Iustin Pop
  the /dev path is dynamic and the unique ID is the UUID generated at
67 a8083063 Iustin Pop
  array creation plus the slave list. For DRBD devices, the /dev path
68 a8083063 Iustin Pop
  is again dynamic and the unique id is the pair (host1, dev1),
69 a8083063 Iustin Pop
  (host2, dev2).
70 a8083063 Iustin Pop

71 a8083063 Iustin Pop
  You can get to a device in two ways:
72 a8083063 Iustin Pop
    - creating the (real) device, which returns you
73 a8083063 Iustin Pop
      an attached instance (lvcreate, mdadm --create)
74 a8083063 Iustin Pop
    - attaching of a python instance to an existing (real) device
75 a8083063 Iustin Pop

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

82 a8083063 Iustin Pop
  """
83 a8083063 Iustin Pop
84 a8083063 Iustin Pop
  STATUS_UNKNOWN = 0
85 a8083063 Iustin Pop
  STATUS_EXISTING = 1
86 a8083063 Iustin Pop
  STATUS_STANDBY = 2
87 a8083063 Iustin Pop
  STATUS_ONLINE = 3
88 a8083063 Iustin Pop
89 a8083063 Iustin Pop
  STATUS_MAP = {
90 a8083063 Iustin Pop
    STATUS_UNKNOWN: "unknown",
91 a8083063 Iustin Pop
    STATUS_EXISTING: "existing",
92 a8083063 Iustin Pop
    STATUS_STANDBY: "ready for use",
93 a8083063 Iustin Pop
    STATUS_ONLINE: "online",
94 a8083063 Iustin Pop
    }
95 a8083063 Iustin Pop
96 a8083063 Iustin Pop
97 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
98 a8083063 Iustin Pop
    self._children = children
99 a8083063 Iustin Pop
    self.dev_path = None
100 a8083063 Iustin Pop
    self.unique_id = unique_id
101 a8083063 Iustin Pop
    self.major = None
102 a8083063 Iustin Pop
    self.minor = None
103 a8083063 Iustin Pop
104 a8083063 Iustin Pop
105 a8083063 Iustin Pop
  def Assemble(self):
106 a8083063 Iustin Pop
    """Assemble the device from its components.
107 a8083063 Iustin Pop

108 a8083063 Iustin Pop
    If this is a plain block device (e.g. LVM) than assemble does
109 a8083063 Iustin Pop
    nothing, as the LVM has no children and we don't put logical
110 a8083063 Iustin Pop
    volumes offline.
111 a8083063 Iustin Pop

112 a8083063 Iustin Pop
    One guarantee is that after the device has been assembled, it
113 a8083063 Iustin Pop
    knows its major/minor numbers. This allows other devices (usually
114 a8083063 Iustin Pop
    parents) to probe correctly for their children.
115 a8083063 Iustin Pop

116 a8083063 Iustin Pop
    """
117 a8083063 Iustin Pop
    status = True
118 a8083063 Iustin Pop
    for child in self._children:
119 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
120 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
121 a8083063 Iustin Pop
      if not status:
122 a8083063 Iustin Pop
        break
123 a8083063 Iustin Pop
      status = status and child.Assemble()
124 a8083063 Iustin Pop
      if not status:
125 a8083063 Iustin Pop
        break
126 a8083063 Iustin Pop
      status = status and child.Open()
127 a8083063 Iustin Pop
128 a8083063 Iustin Pop
    if not status:
129 a8083063 Iustin Pop
      for child in self._children:
130 a8083063 Iustin Pop
        child.Shutdown()
131 a8083063 Iustin Pop
    return status
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
134 a8083063 Iustin Pop
  def Attach(self):
135 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
136 a8083063 Iustin Pop

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

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

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

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

159 a8083063 Iustin Pop
    """
160 a8083063 Iustin Pop
    raise NotImplementedError
161 a8083063 Iustin Pop
162 a8083063 Iustin Pop
163 a8083063 Iustin Pop
  def Remove(self):
164 a8083063 Iustin Pop
    """Remove this device.
165 a8083063 Iustin Pop

166 a8083063 Iustin Pop
    This makes sense only for some of the device types: LV and to a
167 a8083063 Iustin Pop
    lesser degree, md devices. Also note that if the device can't
168 a8083063 Iustin Pop
    attach, the removal can't be completed.
169 a8083063 Iustin Pop

170 a8083063 Iustin Pop
    """
171 a8083063 Iustin Pop
    raise NotImplementedError
172 a8083063 Iustin Pop
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
  def GetStatus(self):
175 a8083063 Iustin Pop
    """Return the status of the device.
176 a8083063 Iustin Pop

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

184 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
185 a8083063 Iustin Pop
    devices need this.
186 a8083063 Iustin Pop

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

190 a8083063 Iustin Pop
    """
191 a8083063 Iustin Pop
    raise NotImplementedError
192 a8083063 Iustin Pop
193 a8083063 Iustin Pop
194 a8083063 Iustin Pop
  def Shutdown(self):
195 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
196 a8083063 Iustin Pop

197 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
198 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
199 a8083063 Iustin Pop
    assembled after this call.
200 a8083063 Iustin Pop

201 a8083063 Iustin Pop
    """
202 a8083063 Iustin Pop
    raise NotImplementedError
203 a8083063 Iustin Pop
204 a8083063 Iustin Pop
205 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
206 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
207 a8083063 Iustin Pop

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

210 a8083063 Iustin Pop
    """
211 a8083063 Iustin Pop
    result = True
212 a8083063 Iustin Pop
    if self._children:
213 a8083063 Iustin Pop
      for child in self._children:
214 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
215 a8083063 Iustin Pop
    return result
216 a8083063 Iustin Pop
217 a8083063 Iustin Pop
218 a8083063 Iustin Pop
  def GetSyncStatus(self):
219 a8083063 Iustin Pop
    """Returns the sync status of the device.
220 a8083063 Iustin Pop

221 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
222 a8083063 Iustin Pop
    status of the mirror.
223 a8083063 Iustin Pop

224 a8083063 Iustin Pop
    Returns:
225 a8083063 Iustin Pop
     (sync_percent, estimated_time, is_degraded)
226 a8083063 Iustin Pop

227 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
228 a8083063 Iustin Pop
    If estimated_time is None, it means we can't estimate
229 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
230 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
231 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
232 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
233 a8083063 Iustin Pop

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

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

245 a8083063 Iustin Pop
    """
246 a8083063 Iustin Pop
    min_percent, max_time, is_degraded = self.GetSyncStatus()
247 a8083063 Iustin Pop
    if self._children:
248 a8083063 Iustin Pop
      for child in self._children:
249 a8083063 Iustin Pop
        c_percent, c_time, c_degraded = child.GetSyncStatus()
250 a8083063 Iustin Pop
        if min_percent is None:
251 a8083063 Iustin Pop
          min_percent = c_percent
252 a8083063 Iustin Pop
        elif c_percent is not None:
253 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
254 a8083063 Iustin Pop
        if max_time is None:
255 a8083063 Iustin Pop
          max_time = c_time
256 a8083063 Iustin Pop
        elif c_time is not None:
257 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
258 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
259 a8083063 Iustin Pop
    return min_percent, max_time, is_degraded
260 a8083063 Iustin Pop
261 a8083063 Iustin Pop
262 a8083063 Iustin Pop
  def __repr__(self):
263 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
264 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
265 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
266 a8083063 Iustin Pop
267 a8083063 Iustin Pop
268 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
269 a8083063 Iustin Pop
  """Logical Volume block device.
270 a8083063 Iustin Pop

271 a8083063 Iustin Pop
  """
272 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
273 a8083063 Iustin Pop
    """Attaches to a LV device.
274 a8083063 Iustin Pop

275 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
276 a8083063 Iustin Pop

277 a8083063 Iustin Pop
    """
278 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
279 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
280 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
281 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
282 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
283 a8083063 Iustin Pop
    self.Attach()
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
  @classmethod
287 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
288 a8083063 Iustin Pop
    """Create a new logical volume.
289 a8083063 Iustin Pop

290 a8083063 Iustin Pop
    """
291 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
292 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
293 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
294 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
295 a8083063 Iustin Pop
    if not pvs_info:
296 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Can't compute PV info for vg %s" %
297 a8083063 Iustin Pop
                                      vg_name)
298 a8083063 Iustin Pop
    pvs_info.sort()
299 a8083063 Iustin Pop
    pvs_info.reverse()
300 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
301 a8083063 Iustin Pop
    if free_size < size:
302 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Not enough free space: required %s,"
303 a8083063 Iustin Pop
                                      " available %s" % (size, free_size))
304 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
305 a8083063 Iustin Pop
                           vg_name, pv_name])
306 a8083063 Iustin Pop
    if result.failed:
307 a8083063 Iustin Pop
      raise errors.BlockDeviceError(result.fail_reason)
308 a8083063 Iustin Pop
    return LogicalVolume(unique_id, children)
309 a8083063 Iustin Pop
310 a8083063 Iustin Pop
  @staticmethod
311 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
312 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
313 a8083063 Iustin Pop

314 a8083063 Iustin Pop
    Args:
315 a8083063 Iustin Pop
      vg_name: the volume group name
316 a8083063 Iustin Pop

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

343 a8083063 Iustin Pop
    """
344 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
345 a8083063 Iustin Pop
      # the LV does not exist
346 a8083063 Iustin Pop
      return True
347 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
348 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
349 a8083063 Iustin Pop
    if result.failed:
350 a8083063 Iustin Pop
      logger.Error("Can't lvremove: %s" % result.fail_reason)
351 a8083063 Iustin Pop
352 a8083063 Iustin Pop
    return not result.failed
353 a8083063 Iustin Pop
354 a8083063 Iustin Pop
355 a8083063 Iustin Pop
  def Attach(self):
356 a8083063 Iustin Pop
    """Attach to an existing LV.
357 a8083063 Iustin Pop

358 a8083063 Iustin Pop
    This method will try to see if an existing and active LV exists
359 a8083063 Iustin Pop
    which matches the our name. If so, its major/minor will be
360 a8083063 Iustin Pop
    recorded.
361 a8083063 Iustin Pop

362 a8083063 Iustin Pop
    """
363 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
364 a8083063 Iustin Pop
    if result.failed:
365 a8083063 Iustin Pop
      logger.Error("Can't find LV %s: %s" %
366 a8083063 Iustin Pop
                   (self.dev_path, result.fail_reason))
367 a8083063 Iustin Pop
      return False
368 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
369 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
370 a8083063 Iustin Pop
      match_result = match.match(line)
371 a8083063 Iustin Pop
      if match_result:
372 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
373 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
374 a8083063 Iustin Pop
        return True
375 a8083063 Iustin Pop
    return False
376 a8083063 Iustin Pop
377 a8083063 Iustin Pop
378 a8083063 Iustin Pop
  def Assemble(self):
379 a8083063 Iustin Pop
    """Assemble the device.
380 a8083063 Iustin Pop

381 a8083063 Iustin Pop
    This is a no-op for the LV device type. Eventually, we could
382 a8083063 Iustin Pop
    lvchange -ay here if we see that the LV is not active.
383 a8083063 Iustin Pop

384 a8083063 Iustin Pop
    """
385 a8083063 Iustin Pop
    return True
386 a8083063 Iustin Pop
387 a8083063 Iustin Pop
388 a8083063 Iustin Pop
  def Shutdown(self):
389 a8083063 Iustin Pop
    """Shutdown the device.
390 a8083063 Iustin Pop

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

394 a8083063 Iustin Pop
    """
395 a8083063 Iustin Pop
    return True
396 a8083063 Iustin Pop
397 a8083063 Iustin Pop
398 a8083063 Iustin Pop
  def GetStatus(self):
399 a8083063 Iustin Pop
    """Return the status of the device.
400 a8083063 Iustin Pop

401 a8083063 Iustin Pop
    Logical volumes will can be in all four states, although we don't
402 a8083063 Iustin Pop
    deactivate (lvchange -an) them when shutdown, so STATUS_EXISTING
403 a8083063 Iustin Pop
    should not be seen for our devices.
404 a8083063 Iustin Pop

405 a8083063 Iustin Pop
    """
406 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
407 a8083063 Iustin Pop
    if result.failed:
408 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
409 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
410 a8083063 Iustin Pop
    out = result.stdout.strip()
411 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
412 a8083063 Iustin Pop
    if len(out) != 6:
413 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
414 a8083063 Iustin Pop
    #writable = (out[1] == "w")
415 a8083063 Iustin Pop
    active = (out[4] == "a")
416 a8083063 Iustin Pop
    online = (out[5] == "o")
417 a8083063 Iustin Pop
    if online:
418 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
419 a8083063 Iustin Pop
    elif active:
420 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
421 a8083063 Iustin Pop
    else:
422 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
423 a8083063 Iustin Pop
424 a8083063 Iustin Pop
    return retval
425 a8083063 Iustin Pop
426 a8083063 Iustin Pop
427 a8083063 Iustin Pop
  def Open(self, force=False):
428 a8083063 Iustin Pop
    """Make the device ready for I/O.
429 a8083063 Iustin Pop

430 a8083063 Iustin Pop
    This is a no-op for the LV device type.
431 a8083063 Iustin Pop

432 a8083063 Iustin Pop
    """
433 a8083063 Iustin Pop
    return True
434 a8083063 Iustin Pop
435 a8083063 Iustin Pop
436 a8083063 Iustin Pop
  def Close(self):
437 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
438 a8083063 Iustin Pop

439 a8083063 Iustin Pop
    This is a no-op for the LV device type.
440 a8083063 Iustin Pop

441 a8083063 Iustin Pop
    """
442 a8083063 Iustin Pop
    return True
443 a8083063 Iustin Pop
444 a8083063 Iustin Pop
445 a8083063 Iustin Pop
  def Snapshot(self, size):
446 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
447 a8083063 Iustin Pop

448 a8083063 Iustin Pop
    """
449 a8083063 Iustin Pop
450 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
451 a8083063 Iustin Pop
452 a8083063 Iustin Pop
    # remove existing snapshot if found
453 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
454 a8083063 Iustin Pop
    snap.Remove()
455 a8083063 Iustin Pop
456 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
457 a8083063 Iustin Pop
    if not pvs_info:
458 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Can't compute PV info for vg %s" %
459 a8083063 Iustin Pop
                                      self._vg_name)
460 a8083063 Iustin Pop
    pvs_info.sort()
461 a8083063 Iustin Pop
    pvs_info.reverse()
462 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
463 a8083063 Iustin Pop
    if free_size < size:
464 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Not enough free space: required %s,"
465 a8083063 Iustin Pop
                                      " available %s" % (size, free_size))
466 a8083063 Iustin Pop
467 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
468 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
469 a8083063 Iustin Pop
    if result.failed:
470 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("command: %s error: %s" %
471 a8083063 Iustin Pop
                                      (result.cmd, result.fail_reason))
472 a8083063 Iustin Pop
473 a8083063 Iustin Pop
    return snap_name
474 a8083063 Iustin Pop
475 a8083063 Iustin Pop
476 a8083063 Iustin Pop
class MDRaid1(BlockDev):
477 a8083063 Iustin Pop
  """raid1 device implemented via md.
478 a8083063 Iustin Pop

479 a8083063 Iustin Pop
  """
480 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
481 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
482 a8083063 Iustin Pop
    self.major = 9
483 a8083063 Iustin Pop
    self.Attach()
484 a8083063 Iustin Pop
485 a8083063 Iustin Pop
486 a8083063 Iustin Pop
  def Attach(self):
487 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
488 a8083063 Iustin Pop

489 a8083063 Iustin Pop
    This tries to find a MD array which has the same UUID as our own.
490 a8083063 Iustin Pop

491 a8083063 Iustin Pop
    """
492 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
493 a8083063 Iustin Pop
    if minor is not None:
494 a8083063 Iustin Pop
      self._SetFromMinor(minor)
495 a8083063 Iustin Pop
    else:
496 a8083063 Iustin Pop
      self.minor = None
497 a8083063 Iustin Pop
      self.dev_path = None
498 a8083063 Iustin Pop
499 a8083063 Iustin Pop
    return (minor is not None)
500 a8083063 Iustin Pop
501 a8083063 Iustin Pop
502 a8083063 Iustin Pop
  @staticmethod
503 a8083063 Iustin Pop
  def _GetUsedDevs():
504 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
505 a8083063 Iustin Pop

506 a8083063 Iustin Pop
    It doesn't matter if the used device have other raid level, just
507 a8083063 Iustin Pop
    that they are in use.
508 a8083063 Iustin Pop

509 a8083063 Iustin Pop
    """
510 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
511 a8083063 Iustin Pop
    data = mdstat.readlines()
512 a8083063 Iustin Pop
    mdstat.close()
513 a8083063 Iustin Pop
514 a8083063 Iustin Pop
    used_md = {}
515 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
516 a8083063 Iustin Pop
    for line in data:
517 a8083063 Iustin Pop
      match = valid_line.match(line)
518 a8083063 Iustin Pop
      if match:
519 a8083063 Iustin Pop
        md_no = int(match.group(1))
520 a8083063 Iustin Pop
        used_md[md_no] = line
521 a8083063 Iustin Pop
522 a8083063 Iustin Pop
    return used_md
523 a8083063 Iustin Pop
524 a8083063 Iustin Pop
525 a8083063 Iustin Pop
  @staticmethod
526 a8083063 Iustin Pop
  def _GetDevInfo(minor):
527 a8083063 Iustin Pop
    """Get info about a MD device.
528 a8083063 Iustin Pop

529 a8083063 Iustin Pop
    Currently only uuid is returned.
530 a8083063 Iustin Pop

531 a8083063 Iustin Pop
    """
532 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
533 a8083063 Iustin Pop
    if result.failed:
534 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
535 a8083063 Iustin Pop
      return None
536 a8083063 Iustin Pop
    retval = {}
537 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
538 a8083063 Iustin Pop
      line = line.strip()
539 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
540 a8083063 Iustin Pop
      if kv:
541 a8083063 Iustin Pop
        if kv[0] == "UUID":
542 a8083063 Iustin Pop
          retval["uuid"] = kv[1]
543 a8083063 Iustin Pop
        elif kv[0] == "State":
544 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
545 a8083063 Iustin Pop
    return retval
546 a8083063 Iustin Pop
547 a8083063 Iustin Pop
548 a8083063 Iustin Pop
  @staticmethod
549 a8083063 Iustin Pop
  def _FindUnusedMinor():
550 a8083063 Iustin Pop
    """Compute an unused MD minor.
551 a8083063 Iustin Pop

552 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
553 a8083063 Iustin Pop

554 a8083063 Iustin Pop
    """
555 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
556 a8083063 Iustin Pop
    i = 0
557 a8083063 Iustin Pop
    while i < 256:
558 a8083063 Iustin Pop
      if i not in used_md:
559 a8083063 Iustin Pop
        break
560 a8083063 Iustin Pop
      i += 1
561 a8083063 Iustin Pop
    if i == 256:
562 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
563 a8083063 Iustin Pop
      return None
564 a8083063 Iustin Pop
    return i
565 a8083063 Iustin Pop
566 a8083063 Iustin Pop
567 a8083063 Iustin Pop
  @classmethod
568 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
569 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
570 a8083063 Iustin Pop

571 a8083063 Iustin Pop
    """
572 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
573 a8083063 Iustin Pop
    for minor in md_list:
574 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
575 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
576 a8083063 Iustin Pop
        return minor
577 a8083063 Iustin Pop
    return None
578 a8083063 Iustin Pop
579 a8083063 Iustin Pop
580 a8083063 Iustin Pop
  @classmethod
581 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
582 a8083063 Iustin Pop
    """Create a new MD raid1 array.
583 a8083063 Iustin Pop

584 a8083063 Iustin Pop
    """
585 a8083063 Iustin Pop
    if not isinstance(children, (tuple, list)):
586 a8083063 Iustin Pop
      raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
587 a8083063 Iustin Pop
                       str(children))
588 a8083063 Iustin Pop
    for i in children:
589 a8083063 Iustin Pop
      if not isinstance(i, BlockDev):
590 a8083063 Iustin Pop
        raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
591 a8083063 Iustin Pop
    for i in children:
592 a8083063 Iustin Pop
      result = utils.RunCmd(["mdadm", "--zero-superblock", "--force",
593 a8083063 Iustin Pop
                             i.dev_path])
594 a8083063 Iustin Pop
      if result.failed:
595 a8083063 Iustin Pop
        logger.Error("Can't zero superblock: %s" % result.fail_reason)
596 a8083063 Iustin Pop
        return None
597 a8083063 Iustin Pop
    minor = cls._FindUnusedMinor()
598 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
599 a8083063 Iustin Pop
                           "--auto=yes", "--force", "-l1",
600 a8083063 Iustin Pop
                           "-n%d" % len(children)] +
601 a8083063 Iustin Pop
                          [dev.dev_path for dev in children])
602 a8083063 Iustin Pop
603 a8083063 Iustin Pop
    if result.failed:
604 a8083063 Iustin Pop
      logger.Error("Can't create md: %s" % result.fail_reason)
605 a8083063 Iustin Pop
      return None
606 a8083063 Iustin Pop
    info = cls._GetDevInfo(minor)
607 a8083063 Iustin Pop
    if not info or not "uuid" in info:
608 a8083063 Iustin Pop
      logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
609 a8083063 Iustin Pop
      return None
610 a8083063 Iustin Pop
    return MDRaid1(info["uuid"], children)
611 a8083063 Iustin Pop
612 a8083063 Iustin Pop
613 a8083063 Iustin Pop
  def Remove(self):
614 a8083063 Iustin Pop
    """Stub remove function for MD RAID 1 arrays.
615 a8083063 Iustin Pop

616 a8083063 Iustin Pop
    We don't remove the superblock right now. Mark a to do.
617 a8083063 Iustin Pop

618 a8083063 Iustin Pop
    """
619 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
620 a8083063 Iustin Pop
    return self.Shutdown()
621 a8083063 Iustin Pop
622 a8083063 Iustin Pop
623 a8083063 Iustin Pop
  def AddChild(self, device):
624 a8083063 Iustin Pop
    """Add a new member to the md raid1.
625 a8083063 Iustin Pop

626 a8083063 Iustin Pop
    """
627 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
628 a8083063 Iustin Pop
      raise errors.BlockDeviceError, "Can't attach to device"
629 a8083063 Iustin Pop
    if device.dev_path is None:
630 a8083063 Iustin Pop
      raise errors.BlockDeviceError, "New child is not initialised"
631 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-a", self.dev_path, device.dev_path])
632 a8083063 Iustin Pop
    if result.failed:
633 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Failed to add new device to array: %s" %
634 a8083063 Iustin Pop
                                      result.output)
635 a8083063 Iustin Pop
    new_len = len(self._children) + 1
636 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
637 a8083063 Iustin Pop
    if result.failed:
638 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Can't grow md array: %s" %
639 a8083063 Iustin Pop
                                      result.output)
640 a8083063 Iustin Pop
    self._children.append(device)
641 a8083063 Iustin Pop
642 a8083063 Iustin Pop
643 a8083063 Iustin Pop
  def RemoveChild(self, dev_path):
644 a8083063 Iustin Pop
    """Remove member from the md raid1.
645 a8083063 Iustin Pop

646 a8083063 Iustin Pop
    """
647 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
648 a8083063 Iustin Pop
      raise errors.BlockDeviceError, "Can't attach to device"
649 a8083063 Iustin Pop
    if len(self._children) == 1:
650 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Can't reduce member when only one"
651 a8083063 Iustin Pop
                                      " child left")
652 a8083063 Iustin Pop
    for device in self._children:
653 a8083063 Iustin Pop
      if device.dev_path == dev_path:
654 a8083063 Iustin Pop
        break
655 a8083063 Iustin Pop
    else:
656 a8083063 Iustin Pop
      raise errors.BlockDeviceError, "Can't find child with this path"
657 a8083063 Iustin Pop
    new_len = len(self._children) - 1
658 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-f", self.dev_path, dev_path])
659 a8083063 Iustin Pop
    if result.failed:
660 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Failed to mark device as failed: %s" %
661 a8083063 Iustin Pop
                                      result.output)
662 a8083063 Iustin Pop
663 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
664 a8083063 Iustin Pop
    # superblocks
665 a8083063 Iustin Pop
    time.sleep(0.5)
666 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-r", self.dev_path, dev_path])
667 a8083063 Iustin Pop
    if result.failed:
668 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Failed to remove device from array:"
669 a8083063 Iustin Pop
                                      " %s" % result.output)
670 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
671 a8083063 Iustin Pop
                           "-n", new_len])
672 a8083063 Iustin Pop
    if result.failed:
673 a8083063 Iustin Pop
      raise errors.BlockDeviceError, ("Can't shrink md array: %s" %
674 a8083063 Iustin Pop
                                      result.output)
675 a8083063 Iustin Pop
    self._children.remove(device)
676 a8083063 Iustin Pop
677 a8083063 Iustin Pop
678 a8083063 Iustin Pop
  def GetStatus(self):
679 a8083063 Iustin Pop
    """Return the status of the device.
680 a8083063 Iustin Pop

681 a8083063 Iustin Pop
    """
682 a8083063 Iustin Pop
    self.Attach()
683 a8083063 Iustin Pop
    if self.minor is None:
684 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
685 a8083063 Iustin Pop
    else:
686 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
687 a8083063 Iustin Pop
    return retval
688 a8083063 Iustin Pop
689 a8083063 Iustin Pop
690 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
691 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
692 a8083063 Iustin Pop

693 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
694 a8083063 Iustin Pop

695 a8083063 Iustin Pop
    """
696 a8083063 Iustin Pop
    self.minor = minor
697 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
698 a8083063 Iustin Pop
699 a8083063 Iustin Pop
700 a8083063 Iustin Pop
  def Assemble(self):
701 a8083063 Iustin Pop
    """Assemble the MD device.
702 a8083063 Iustin Pop

703 a8083063 Iustin Pop
    At this point we should have:
704 a8083063 Iustin Pop
      - list of children devices
705 a8083063 Iustin Pop
      - uuid
706 a8083063 Iustin Pop

707 a8083063 Iustin Pop
    """
708 a8083063 Iustin Pop
    result = super(MDRaid1, self).Assemble()
709 a8083063 Iustin Pop
    if not result:
710 a8083063 Iustin Pop
      return result
711 a8083063 Iustin Pop
    md_list = self._GetUsedDevs()
712 a8083063 Iustin Pop
    for minor in md_list:
713 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
714 a8083063 Iustin Pop
      if info and info["uuid"] == self.unique_id:
715 a8083063 Iustin Pop
        self._SetFromMinor(minor)
716 a8083063 Iustin Pop
        logger.Info("MD array %s already started" % str(self))
717 a8083063 Iustin Pop
        return True
718 a8083063 Iustin Pop
    free_minor = self._FindUnusedMinor()
719 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
720 a8083063 Iustin Pop
                           self.unique_id, "/dev/md%d" % free_minor] +
721 a8083063 Iustin Pop
                          [bdev.dev_path for bdev in self._children])
722 a8083063 Iustin Pop
    if result.failed:
723 a8083063 Iustin Pop
      logger.Error("Can't assemble MD array: %s" % result.fail_reason)
724 a8083063 Iustin Pop
      self.minor = None
725 a8083063 Iustin Pop
    else:
726 a8083063 Iustin Pop
      self.minor = free_minor
727 a8083063 Iustin Pop
    return not result.failed
728 a8083063 Iustin Pop
729 a8083063 Iustin Pop
730 a8083063 Iustin Pop
  def Shutdown(self):
731 a8083063 Iustin Pop
    """Tear down the MD array.
732 a8083063 Iustin Pop

733 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
734 a8083063 Iustin Pop
    longer available.
735 a8083063 Iustin Pop

736 a8083063 Iustin Pop
    """
737 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
738 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
739 a8083063 Iustin Pop
      return True
740 a8083063 Iustin Pop
741 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
742 a8083063 Iustin Pop
    if result.failed:
743 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
744 a8083063 Iustin Pop
      return False
745 a8083063 Iustin Pop
    self.minor = None
746 a8083063 Iustin Pop
    self.dev_path = None
747 a8083063 Iustin Pop
    return True
748 a8083063 Iustin Pop
749 a8083063 Iustin Pop
750 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
751 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
752 a8083063 Iustin Pop

753 a8083063 Iustin Pop
    """
754 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
755 a8083063 Iustin Pop
    if self.minor is None:
756 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
757 a8083063 Iustin Pop
      return False
758 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
759 a8083063 Iustin Pop
    try:
760 a8083063 Iustin Pop
      f.write("%d" % kbytes)
761 a8083063 Iustin Pop
    finally:
762 a8083063 Iustin Pop
      f.close()
763 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
764 a8083063 Iustin Pop
    try:
765 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
766 a8083063 Iustin Pop
    finally:
767 a8083063 Iustin Pop
      f.close()
768 a8083063 Iustin Pop
    return result
769 a8083063 Iustin Pop
770 a8083063 Iustin Pop
771 a8083063 Iustin Pop
  def GetSyncStatus(self):
772 a8083063 Iustin Pop
    """Returns the sync status of the device.
773 a8083063 Iustin Pop

774 a8083063 Iustin Pop
    Returns:
775 a8083063 Iustin Pop
     (sync_percent, estimated_time)
776 a8083063 Iustin Pop

777 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
778 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
779 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
780 a8083063 Iustin Pop

781 a8083063 Iustin Pop
    """
782 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
783 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
784 a8083063 Iustin Pop
    dev_info = self._GetDevInfo(self.minor)
785 a8083063 Iustin Pop
    is_clean = ("state" in dev_info and
786 a8083063 Iustin Pop
                len(dev_info["state"]) == 1 and
787 a8083063 Iustin Pop
                dev_info["state"][0] in ("clean", "active"))
788 a8083063 Iustin Pop
    sys_path = "/sys/block/md%s/md/" % self.minor
789 a8083063 Iustin Pop
    f = file(sys_path + "sync_action")
790 a8083063 Iustin Pop
    sync_status = f.readline().strip()
791 a8083063 Iustin Pop
    f.close()
792 a8083063 Iustin Pop
    if sync_status == "idle":
793 a8083063 Iustin Pop
      return None, None, not is_clean
794 a8083063 Iustin Pop
    f = file(sys_path + "sync_completed")
795 a8083063 Iustin Pop
    sync_completed = f.readline().strip().split(" / ")
796 a8083063 Iustin Pop
    f.close()
797 a8083063 Iustin Pop
    if len(sync_completed) != 2:
798 a8083063 Iustin Pop
      return 0, None, not is_clean
799 a8083063 Iustin Pop
    sync_done, sync_total = [float(i) for i in sync_completed]
800 a8083063 Iustin Pop
    sync_percent = 100.0*sync_done/sync_total
801 a8083063 Iustin Pop
    f = file(sys_path + "sync_speed")
802 a8083063 Iustin Pop
    sync_speed_k = int(f.readline().strip())
803 a8083063 Iustin Pop
    if sync_speed_k == 0:
804 a8083063 Iustin Pop
      time_est = None
805 a8083063 Iustin Pop
    else:
806 a8083063 Iustin Pop
      time_est = (sync_total - sync_done) / 2 / sync_speed_k
807 a8083063 Iustin Pop
    return sync_percent, time_est, not is_clean
808 a8083063 Iustin Pop
809 a8083063 Iustin Pop
810 a8083063 Iustin Pop
  def Open(self, force=False):
811 a8083063 Iustin Pop
    """Make the device ready for I/O.
812 a8083063 Iustin Pop

813 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, although we could use
814 a8083063 Iustin Pop
    the 2.6.18's new array_state thing.
815 a8083063 Iustin Pop

816 a8083063 Iustin Pop
    """
817 a8083063 Iustin Pop
    return True
818 a8083063 Iustin Pop
819 a8083063 Iustin Pop
820 a8083063 Iustin Pop
  def Close(self):
821 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
822 a8083063 Iustin Pop

823 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
824 a8083063 Iustin Pop
    `Open()`.
825 a8083063 Iustin Pop

826 a8083063 Iustin Pop
    """
827 a8083063 Iustin Pop
    return True
828 a8083063 Iustin Pop
829 a8083063 Iustin Pop
830 a8083063 Iustin Pop
831 a8083063 Iustin Pop
class DRBDev(BlockDev):
832 a8083063 Iustin Pop
  """DRBD block device.
833 a8083063 Iustin Pop

834 a8083063 Iustin Pop
  This implements the local host part of the DRBD device, i.e. it
835 a8083063 Iustin Pop
  doesn't do anything to the supposed peer. If you need a fully
836 a8083063 Iustin Pop
  connected DRBD pair, you need to use this class on both hosts.
837 a8083063 Iustin Pop

838 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
839 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
840 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
841 a8083063 Iustin Pop
  valid size and is zeroed on create.
842 a8083063 Iustin Pop

843 a8083063 Iustin Pop
  """
844 a8083063 Iustin Pop
  _DRBD_MAJOR = 147
845 a8083063 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
846 a8083063 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
847 a8083063 Iustin Pop
  _ST_CONNECTED = "Connected"
848 a8083063 Iustin Pop
849 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
850 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
851 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
852 a8083063 Iustin Pop
    if len(children) != 2:
853 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
854 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
855 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
856 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
857 a8083063 Iustin Pop
    self.Attach()
858 a8083063 Iustin Pop
859 a8083063 Iustin Pop
  @staticmethod
860 a8083063 Iustin Pop
  def _DevPath(minor):
861 a8083063 Iustin Pop
    """Return the path to a drbd device for a given minor.
862 a8083063 Iustin Pop

863 a8083063 Iustin Pop
    """
864 a8083063 Iustin Pop
    return "/dev/drbd%d" % minor
865 a8083063 Iustin Pop
866 a8083063 Iustin Pop
  @staticmethod
867 a8083063 Iustin Pop
  def _GetProcData():
868 a8083063 Iustin Pop
    """Return data from /proc/drbd.
869 a8083063 Iustin Pop

870 a8083063 Iustin Pop
    """
871 a8083063 Iustin Pop
    stat = open("/proc/drbd", "r")
872 a8083063 Iustin Pop
    data = stat.read().splitlines()
873 a8083063 Iustin Pop
    stat.close()
874 a8083063 Iustin Pop
    return data
875 a8083063 Iustin Pop
876 a8083063 Iustin Pop
877 a8083063 Iustin Pop
  @classmethod
878 a8083063 Iustin Pop
  def _GetUsedDevs(cls):
879 a8083063 Iustin Pop
    """Compute the list of used DRBD devices.
880 a8083063 Iustin Pop

881 a8083063 Iustin Pop
    """
882 a8083063 Iustin Pop
    data = cls._GetProcData()
883 a8083063 Iustin Pop
884 a8083063 Iustin Pop
    used_devs = {}
885 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
886 a8083063 Iustin Pop
    for line in data:
887 a8083063 Iustin Pop
      match = valid_line.match(line)
888 a8083063 Iustin Pop
      if not match:
889 a8083063 Iustin Pop
        continue
890 a8083063 Iustin Pop
      minor = int(match.group(1))
891 a8083063 Iustin Pop
      state = match.group(2)
892 a8083063 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
893 a8083063 Iustin Pop
        continue
894 a8083063 Iustin Pop
      used_devs[minor] = state, line
895 a8083063 Iustin Pop
896 a8083063 Iustin Pop
    return used_devs
897 a8083063 Iustin Pop
898 a8083063 Iustin Pop
899 a8083063 Iustin Pop
  @classmethod
900 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
901 a8083063 Iustin Pop
    """Find an unused DRBD device.
902 a8083063 Iustin Pop

903 a8083063 Iustin Pop
    """
904 a8083063 Iustin Pop
    data = cls._GetProcData()
905 a8083063 Iustin Pop
906 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
907 a8083063 Iustin Pop
    for line in data:
908 a8083063 Iustin Pop
      match = valid_line.match(line)
909 a8083063 Iustin Pop
      if match:
910 a8083063 Iustin Pop
        return int(match.group(1))
911 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
912 a8083063 Iustin Pop
    return None
913 a8083063 Iustin Pop
914 a8083063 Iustin Pop
915 a8083063 Iustin Pop
  @classmethod
916 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
917 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
918 a8083063 Iustin Pop

919 a8083063 Iustin Pop
    This return, if available, the local backing device in (major,
920 a8083063 Iustin Pop
    minor) formant and the local and remote (ip, port) information.
921 a8083063 Iustin Pop

922 a8083063 Iustin Pop
    """
923 a8083063 Iustin Pop
    data = {}
924 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
925 a8083063 Iustin Pop
    if result.failed:
926 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
927 a8083063 Iustin Pop
      return data
928 a8083063 Iustin Pop
    out = result.stdout
929 a8083063 Iustin Pop
    if out == "Not configured\n":
930 a8083063 Iustin Pop
      return data
931 a8083063 Iustin Pop
    for line in out.splitlines():
932 a8083063 Iustin Pop
      if "local_dev" not in data:
933 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
934 a8083063 Iustin Pop
        if match:
935 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
936 a8083063 Iustin Pop
          continue
937 a8083063 Iustin Pop
      if "meta_dev" not in data:
938 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
939 a8083063 Iustin Pop
        if match:
940 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
941 a8083063 Iustin Pop
            # matched on the major/minor
942 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
943 a8083063 Iustin Pop
          else:
944 a8083063 Iustin Pop
            # matched on the "internal" string
945 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
946 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
947 a8083063 Iustin Pop
            data["meta_index"] = -1
948 a8083063 Iustin Pop
          continue
949 a8083063 Iustin Pop
      if "meta_index" not in data:
950 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
951 a8083063 Iustin Pop
        if match:
952 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
953 a8083063 Iustin Pop
          continue
954 a8083063 Iustin Pop
      if "local_addr" not in data:
955 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
956 a8083063 Iustin Pop
        if match:
957 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
958 a8083063 Iustin Pop
          continue
959 a8083063 Iustin Pop
      if "remote_addr" not in data:
960 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
961 a8083063 Iustin Pop
        if match:
962 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
963 a8083063 Iustin Pop
          continue
964 a8083063 Iustin Pop
    return data
965 a8083063 Iustin Pop
966 a8083063 Iustin Pop
967 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
968 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
969 a8083063 Iustin Pop

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

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

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

1003 a8083063 Iustin Pop
    """
1004 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1005 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1006 a8083063 Iustin Pop
      return True
1007 a8083063 Iustin Pop
1008 a8083063 Iustin Pop
    if self._lhost is None:
1009 a8083063 Iustin Pop
      return False
1010 a8083063 Iustin Pop
1011 a8083063 Iustin Pop
    if not ("local_addr" in info and
1012 a8083063 Iustin Pop
            "remote_addr" in info):
1013 a8083063 Iustin Pop
      return False
1014 a8083063 Iustin Pop
1015 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1016 a8083063 Iustin Pop
    retval = (retval and
1017 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1018 a8083063 Iustin Pop
    return retval
1019 a8083063 Iustin Pop
1020 a8083063 Iustin Pop
1021 a8083063 Iustin Pop
  @staticmethod
1022 a8083063 Iustin Pop
  def _IsValidMeta(meta_device):
1023 a8083063 Iustin Pop
    """Check if the given meta device looks like a valid one.
1024 a8083063 Iustin Pop

1025 a8083063 Iustin Pop
    This currently only check the size, which must be around
1026 a8083063 Iustin Pop
    128MiB.
1027 a8083063 Iustin Pop

1028 a8083063 Iustin Pop
    """
1029 a8083063 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1030 a8083063 Iustin Pop
    if result.failed:
1031 a8083063 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1032 a8083063 Iustin Pop
      return False
1033 a8083063 Iustin Pop
    try:
1034 a8083063 Iustin Pop
      sectors = int(result.stdout)
1035 a8083063 Iustin Pop
    except ValueError:
1036 a8083063 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1037 a8083063 Iustin Pop
      return False
1038 a8083063 Iustin Pop
    bytes = sectors * 512
1039 a8083063 Iustin Pop
    if bytes < 128*1024*1024: # less than 128MiB
1040 a8083063 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes/1024/1024))
1041 a8083063 Iustin Pop
      return False
1042 a8083063 Iustin Pop
    if bytes > (128+32)*1024*1024: # account for an extra (big) PE on LVM
1043 a8083063 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes/1024/1024))
1044 a8083063 Iustin Pop
      return False
1045 a8083063 Iustin Pop
    return True
1046 a8083063 Iustin Pop
1047 a8083063 Iustin Pop
1048 a8083063 Iustin Pop
  @classmethod
1049 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1050 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1051 a8083063 Iustin Pop

1052 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1053 a8083063 Iustin Pop
    device. And it must be done only once.
1054 a8083063 Iustin Pop

1055 a8083063 Iustin Pop
    """
1056 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta):
1057 a8083063 Iustin Pop
      return False
1058 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1059 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1060 a8083063 Iustin Pop
    if result.failed:
1061 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1062 a8083063 Iustin Pop
    return not result.failed
1063 a8083063 Iustin Pop
1064 a8083063 Iustin Pop
1065 a8083063 Iustin Pop
  @classmethod
1066 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1067 a8083063 Iustin Pop
    """Detach from the local device.
1068 a8083063 Iustin Pop

1069 a8083063 Iustin Pop
    I/Os will continue to be served from the remote device. If we
1070 a8083063 Iustin Pop
    don't have a remote device, this operation will fail.
1071 a8083063 Iustin Pop

1072 a8083063 Iustin Pop
    """
1073 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1074 a8083063 Iustin Pop
    if result.failed:
1075 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1076 a8083063 Iustin Pop
    return not result.failed
1077 a8083063 Iustin Pop
1078 a8083063 Iustin Pop
1079 a8083063 Iustin Pop
  @staticmethod
1080 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1081 a8083063 Iustin Pop
    """Deactivate the device.
1082 a8083063 Iustin Pop

1083 a8083063 Iustin Pop
    This will, of course, fail if the device is in use.
1084 a8083063 Iustin Pop

1085 a8083063 Iustin Pop
    """
1086 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1087 a8083063 Iustin Pop
    if result.failed:
1088 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1089 a8083063 Iustin Pop
    return not result.failed
1090 a8083063 Iustin Pop
1091 a8083063 Iustin Pop
1092 a8083063 Iustin Pop
  @classmethod
1093 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1094 a8083063 Iustin Pop
    """Configure the network part of the device.
1095 a8083063 Iustin Pop

1096 a8083063 Iustin Pop
    This operation can be, in theory, done multiple times, but there
1097 a8083063 Iustin Pop
    have been cases (in lab testing) in which the network part of the
1098 a8083063 Iustin Pop
    device had become stuck and couldn't be shut down because activity
1099 a8083063 Iustin Pop
    from the new peer (also stuck) triggered a timer re-init and
1100 a8083063 Iustin Pop
    needed remote peer interface shutdown in order to clear. So please
1101 a8083063 Iustin Pop
    don't change online the net config.
1102 a8083063 Iustin Pop

1103 a8083063 Iustin Pop
    """
1104 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1105 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1106 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1107 a8083063 Iustin Pop
                           protocol])
1108 a8083063 Iustin Pop
    if result.failed:
1109 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1110 a8083063 Iustin Pop
                   result.fail_reason)
1111 a8083063 Iustin Pop
      return False
1112 a8083063 Iustin Pop
1113 a8083063 Iustin Pop
    timeout = time.time() + 10
1114 a8083063 Iustin Pop
    ok = False
1115 a8083063 Iustin Pop
    while time.time() < timeout:
1116 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1117 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1118 a8083063 Iustin Pop
        time.sleep(1)
1119 a8083063 Iustin Pop
        continue
1120 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1121 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1122 a8083063 Iustin Pop
        time.sleep(1)
1123 a8083063 Iustin Pop
        continue
1124 a8083063 Iustin Pop
      ok = True
1125 a8083063 Iustin Pop
      break
1126 a8083063 Iustin Pop
    if not ok:
1127 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1128 a8083063 Iustin Pop
      return False
1129 a8083063 Iustin Pop
    return True
1130 a8083063 Iustin Pop
1131 a8083063 Iustin Pop
1132 a8083063 Iustin Pop
  @classmethod
1133 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1134 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1135 a8083063 Iustin Pop

1136 a8083063 Iustin Pop
    This fails if we don't have a local device.
1137 a8083063 Iustin Pop

1138 a8083063 Iustin Pop
    """
1139 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1140 a8083063 Iustin Pop
    logger.Error("Can't shutdown network: %s" % result.output)
1141 a8083063 Iustin Pop
    return not result.failed
1142 a8083063 Iustin Pop
1143 a8083063 Iustin Pop
1144 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
1145 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
1146 a8083063 Iustin Pop

1147 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
1148 a8083063 Iustin Pop

1149 a8083063 Iustin Pop
    """
1150 a8083063 Iustin Pop
    if minor is None:
1151 a8083063 Iustin Pop
      self.minor = self.dev_path = None
1152 a8083063 Iustin Pop
    else:
1153 a8083063 Iustin Pop
      self.minor = minor
1154 a8083063 Iustin Pop
      self.dev_path = self._DevPath(minor)
1155 a8083063 Iustin Pop
1156 a8083063 Iustin Pop
1157 a8083063 Iustin Pop
  def Assemble(self):
1158 a8083063 Iustin Pop
    """Assemble the drbd.
1159 a8083063 Iustin Pop

1160 a8083063 Iustin Pop
    Method:
1161 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1162 a8083063 Iustin Pop
        - checking the list of used drbd devices
1163 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1164 a8083063 Iustin Pop
        - if yes, abort?
1165 a8083063 Iustin Pop
        - if not, bind
1166 a8083063 Iustin Pop
      - if we have a local/remote net info:
1167 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1168 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1169 a8083063 Iustin Pop
          if yes abort
1170 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1171 a8083063 Iustin Pop
          port, if yes abort (for now)
1172 a8083063 Iustin Pop
        - bind our net port
1173 a8083063 Iustin Pop
        - bind the remote net port
1174 a8083063 Iustin Pop

1175 a8083063 Iustin Pop
    """
1176 a8083063 Iustin Pop
    self.Attach()
1177 a8083063 Iustin Pop
    if self.minor is not None:
1178 a8083063 Iustin Pop
      logger.Info("Already assembled")
1179 a8083063 Iustin Pop
      return True
1180 a8083063 Iustin Pop
1181 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1182 a8083063 Iustin Pop
    if not result:
1183 a8083063 Iustin Pop
      return result
1184 a8083063 Iustin Pop
1185 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1186 a8083063 Iustin Pop
    if minor is None:
1187 a8083063 Iustin Pop
      raise errors.BlockDeviceError, "Not enough free minors for DRBD!"
1188 a8083063 Iustin Pop
    need_localdev_teardown = False
1189 a8083063 Iustin Pop
    if self._children[0]:
1190 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1191 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1192 a8083063 Iustin Pop
      if not result:
1193 a8083063 Iustin Pop
        return False
1194 a8083063 Iustin Pop
      need_localdev_teardown = True
1195 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1196 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1197 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1198 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1199 a8083063 Iustin Pop
                                 "C")
1200 a8083063 Iustin Pop
      if not result:
1201 a8083063 Iustin Pop
        if need_localdev_teardown:
1202 a8083063 Iustin Pop
          # we will ignore failures from this
1203 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1204 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1205 a8083063 Iustin Pop
        return False
1206 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1207 a8083063 Iustin Pop
    return True
1208 a8083063 Iustin Pop
1209 a8083063 Iustin Pop
1210 a8083063 Iustin Pop
  def Shutdown(self):
1211 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1212 a8083063 Iustin Pop

1213 a8083063 Iustin Pop
    """
1214 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1215 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1216 a8083063 Iustin Pop
      return True
1217 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1218 a8083063 Iustin Pop
      return False
1219 a8083063 Iustin Pop
    self.minor = None
1220 a8083063 Iustin Pop
    self.dev_path = None
1221 a8083063 Iustin Pop
    return True
1222 a8083063 Iustin Pop
1223 a8083063 Iustin Pop
1224 a8083063 Iustin Pop
  def Attach(self):
1225 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1226 a8083063 Iustin Pop

1227 a8083063 Iustin Pop
    In case of partially attached (local device matches but no network
1228 a8083063 Iustin Pop
    setup), we perform the network attach. If successful, we re-test
1229 a8083063 Iustin Pop
    the attach if can return success.
1230 a8083063 Iustin Pop

1231 a8083063 Iustin Pop
    """
1232 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1233 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1234 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1235 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1236 a8083063 Iustin Pop
      if match_l and match_r:
1237 a8083063 Iustin Pop
        break
1238 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1239 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1240 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1241 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1242 a8083063 Iustin Pop
                                  "C")
1243 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1244 a8083063 Iustin Pop
          break
1245 a8083063 Iustin Pop
    else:
1246 a8083063 Iustin Pop
      minor = None
1247 a8083063 Iustin Pop
1248 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1249 a8083063 Iustin Pop
    return minor is not None
1250 a8083063 Iustin Pop
1251 a8083063 Iustin Pop
1252 a8083063 Iustin Pop
  def Open(self, force=False):
1253 a8083063 Iustin Pop
    """Make the local state primary.
1254 a8083063 Iustin Pop

1255 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1256 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1257 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1258 a8083063 Iustin Pop
    has to be given.
1259 a8083063 Iustin Pop

1260 a8083063 Iustin Pop
    """
1261 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1262 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1263 a8083063 Iustin Pop
      return False
1264 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1265 a8083063 Iustin Pop
    if force:
1266 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1267 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1268 a8083063 Iustin Pop
    if result.failed:
1269 a8083063 Iustin Pop
      logger.Error("Can't make drbd device primary: %s" % result.output)
1270 a8083063 Iustin Pop
      return False
1271 a8083063 Iustin Pop
    return True
1272 a8083063 Iustin Pop
1273 a8083063 Iustin Pop
1274 a8083063 Iustin Pop
  def Close(self):
1275 a8083063 Iustin Pop
    """Make the local state secondary.
1276 a8083063 Iustin Pop

1277 a8083063 Iustin Pop
    This will, of course, fail if the device is in use.
1278 a8083063 Iustin Pop

1279 a8083063 Iustin Pop
    """
1280 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1281 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1282 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1283 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1284 a8083063 Iustin Pop
    if result.failed:
1285 a8083063 Iustin Pop
      logger.Error("Can't switch drbd device to secondary: %s" % result.output)
1286 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't switch drbd device to secondary")
1287 a8083063 Iustin Pop
1288 a8083063 Iustin Pop
1289 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1290 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1291 a8083063 Iustin Pop

1292 a8083063 Iustin Pop
    """
1293 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1294 a8083063 Iustin Pop
    if self.minor is None:
1295 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1296 a8083063 Iustin Pop
      return False
1297 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1298 a8083063 Iustin Pop
                           kbytes])
1299 a8083063 Iustin Pop
    if result.failed:
1300 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1301 a8083063 Iustin Pop
    return not result.failed and children_result
1302 a8083063 Iustin Pop
1303 a8083063 Iustin Pop
1304 a8083063 Iustin Pop
  def GetSyncStatus(self):
1305 a8083063 Iustin Pop
    """Returns the sync status of the device.
1306 a8083063 Iustin Pop

1307 a8083063 Iustin Pop
    Returns:
1308 a8083063 Iustin Pop
     (sync_percent, estimated_time)
1309 a8083063 Iustin Pop

1310 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1311 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1312 a8083063 Iustin Pop
    the time needed, otherwise it's the time left in seconds
1313 a8083063 Iustin Pop

1314 a8083063 Iustin Pop
    """
1315 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1316 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1317 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1318 a8083063 Iustin Pop
    if self.minor not in proc_info:
1319 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1320 a8083063 Iustin Pop
                                    self.minor)
1321 a8083063 Iustin Pop
    line = proc_info[self.minor]
1322 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1323 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1324 a8083063 Iustin Pop
    if match:
1325 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1326 a8083063 Iustin Pop
      hours = int(match.group(2))
1327 a8083063 Iustin Pop
      minutes = int(match.group(3))
1328 a8083063 Iustin Pop
      seconds = int(match.group(4))
1329 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1330 a8083063 Iustin Pop
    else:
1331 a8083063 Iustin Pop
      sync_percent = None
1332 a8083063 Iustin Pop
      est_time = None
1333 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1334 a8083063 Iustin Pop
    if not match:
1335 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1336 a8083063 Iustin Pop
                                    self.minor)
1337 a8083063 Iustin Pop
    client_state = match.group(1)
1338 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1339 a8083063 Iustin Pop
    return sync_percent, est_time, is_degraded
1340 a8083063 Iustin Pop
1341 a8083063 Iustin Pop
1342 a8083063 Iustin Pop
  @staticmethod
1343 a8083063 Iustin Pop
  def _MassageProcData(data):
1344 a8083063 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1345 a8083063 Iustin Pop

1346 a8083063 Iustin Pop
    Returns:
1347 a8083063 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
1348 a8083063 Iustin Pop

1349 a8083063 Iustin Pop
    """
1350 a8083063 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1351 a8083063 Iustin Pop
    results = {}
1352 a8083063 Iustin Pop
    old_minor = old_line = None
1353 a8083063 Iustin Pop
    for line in data:
1354 a8083063 Iustin Pop
      lresult = lmatch.match(line)
1355 a8083063 Iustin Pop
      if lresult is not None:
1356 a8083063 Iustin Pop
        if old_minor is not None:
1357 a8083063 Iustin Pop
          results[old_minor] = old_line
1358 a8083063 Iustin Pop
        old_minor = int(lresult.group(1))
1359 a8083063 Iustin Pop
        old_line = line
1360 a8083063 Iustin Pop
      else:
1361 a8083063 Iustin Pop
        if old_minor is not None:
1362 a8083063 Iustin Pop
          old_line += " " + line.strip()
1363 a8083063 Iustin Pop
    # add last line
1364 a8083063 Iustin Pop
    if old_minor is not None:
1365 a8083063 Iustin Pop
      results[old_minor] = old_line
1366 a8083063 Iustin Pop
    return results
1367 a8083063 Iustin Pop
1368 a8083063 Iustin Pop
1369 a8083063 Iustin Pop
  def GetStatus(self):
1370 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1371 a8083063 Iustin Pop

1372 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1373 a8083063 Iustin Pop

1374 a8083063 Iustin Pop
    """
1375 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1376 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1377 a8083063 Iustin Pop
1378 a8083063 Iustin Pop
    data = self._GetProcData()
1379 a8083063 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1380 a8083063 Iustin Pop
                       self.minor)
1381 a8083063 Iustin Pop
    for line in data:
1382 a8083063 Iustin Pop
      mresult = match.match(line)
1383 a8083063 Iustin Pop
      if mresult:
1384 a8083063 Iustin Pop
        break
1385 a8083063 Iustin Pop
    else:
1386 a8083063 Iustin Pop
      logger.Error("Can't find myself!")
1387 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1388 a8083063 Iustin Pop
1389 a8083063 Iustin Pop
    state = mresult.group(2)
1390 a8083063 Iustin Pop
    if state == "Primary":
1391 a8083063 Iustin Pop
      result = self.STATUS_ONLINE
1392 a8083063 Iustin Pop
    else:
1393 a8083063 Iustin Pop
      result = self.STATUS_STANDBY
1394 a8083063 Iustin Pop
1395 a8083063 Iustin Pop
    return result
1396 a8083063 Iustin Pop
1397 a8083063 Iustin Pop
1398 a8083063 Iustin Pop
  @staticmethod
1399 a8083063 Iustin Pop
  def _ZeroDevice(device):
1400 a8083063 Iustin Pop
    """Zero a device.
1401 a8083063 Iustin Pop

1402 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1403 a8083063 Iustin Pop

1404 a8083063 Iustin Pop
    """
1405 a8083063 Iustin Pop
    f = open(device, "w")
1406 a8083063 Iustin Pop
    buf = "\0" * 1048576
1407 a8083063 Iustin Pop
    try:
1408 a8083063 Iustin Pop
      while True:
1409 a8083063 Iustin Pop
        f.write(buf)
1410 a8083063 Iustin Pop
    except IOError, err:
1411 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1412 a8083063 Iustin Pop
        raise
1413 a8083063 Iustin Pop
1414 a8083063 Iustin Pop
1415 a8083063 Iustin Pop
  @classmethod
1416 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1417 a8083063 Iustin Pop
    """Create a new DRBD device.
1418 a8083063 Iustin Pop

1419 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1420 a8083063 Iustin Pop
    function just zeroes the meta device.
1421 a8083063 Iustin Pop

1422 a8083063 Iustin Pop
    """
1423 a8083063 Iustin Pop
    if len(children) != 2:
1424 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1425 a8083063 Iustin Pop
    meta = children[1]
1426 a8083063 Iustin Pop
    meta.Assemble()
1427 a8083063 Iustin Pop
    if not meta.Attach():
1428 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1429 a8083063 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1430 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1431 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1432 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1433 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1434 a8083063 Iustin Pop
    return cls(unique_id, children)
1435 a8083063 Iustin Pop
1436 a8083063 Iustin Pop
1437 a8083063 Iustin Pop
  def Remove(self):
1438 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1439 a8083063 Iustin Pop

1440 a8083063 Iustin Pop
    """
1441 a8083063 Iustin Pop
    return self.Shutdown()
1442 a8083063 Iustin Pop
1443 a8083063 Iustin Pop
1444 a8083063 Iustin Pop
DEV_MAP = {
1445 a8083063 Iustin Pop
  "lvm": LogicalVolume,
1446 a8083063 Iustin Pop
  "md_raid1": MDRaid1,
1447 a8083063 Iustin Pop
  "drbd": DRBDev,
1448 a8083063 Iustin Pop
  }
1449 a8083063 Iustin Pop
1450 a8083063 Iustin Pop
1451 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1452 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1453 a8083063 Iustin Pop

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

1457 a8083063 Iustin Pop
  """
1458 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1459 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1460 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1461 a8083063 Iustin Pop
  if not device.Attach():
1462 a8083063 Iustin Pop
    return None
1463 a8083063 Iustin Pop
  return  device
1464 a8083063 Iustin Pop
1465 a8083063 Iustin Pop
1466 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1467 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1468 a8083063 Iustin Pop

1469 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1470 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1471 a8083063 Iustin Pop

1472 a8083063 Iustin Pop
  """
1473 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1474 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1475 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1476 a8083063 Iustin Pop
  if not device.Attach():
1477 a8083063 Iustin Pop
    device.Assemble()
1478 a8083063 Iustin Pop
  if not device.Attach():
1479 a8083063 Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
1480 a8083063 Iustin Pop
                                  " %s/%s/%s" %
1481 a8083063 Iustin Pop
                                  (dev_type, unique_id, children))
1482 a8083063 Iustin Pop
  return device
1483 a8083063 Iustin Pop
1484 a8083063 Iustin Pop
1485 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1486 a8083063 Iustin Pop
  """Create a device.
1487 a8083063 Iustin Pop

1488 a8083063 Iustin Pop
  """
1489 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1490 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1491 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1492 a8083063 Iustin Pop
  return device