Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 17dfc522

History | View | Annotate | Download (73.3 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 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from ganeti import utils
30 a8083063 Iustin Pop
from ganeti import logger
31 a8083063 Iustin Pop
from ganeti import errors
32 fe96220b Iustin Pop
from ganeti import constants
33 a8083063 Iustin Pop
34 a8083063 Iustin Pop
35 a8083063 Iustin Pop
class BlockDev(object):
36 a8083063 Iustin Pop
  """Block device abstract class.
37 a8083063 Iustin Pop

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

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

50 a8083063 Iustin Pop
  The many different states of the device are due to the fact that we
51 a8083063 Iustin Pop
  need to cover many device types:
52 a8083063 Iustin Pop
    - logical volumes are created, lvchange -a y $lv, and used
53 a8083063 Iustin Pop
    - md arrays are created or assembled 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
  The status of the device can be examined by `GetStatus()`, which
57 a8083063 Iustin Pop
  returns a numerical value, depending on the position in the
58 a8083063 Iustin Pop
  transition stack of the device.
59 a8083063 Iustin Pop

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

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

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

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

84 a8083063 Iustin Pop
  """
85 a8083063 Iustin Pop
  STATUS_UNKNOWN = 0
86 a8083063 Iustin Pop
  STATUS_EXISTING = 1
87 a8083063 Iustin Pop
  STATUS_STANDBY = 2
88 a8083063 Iustin Pop
  STATUS_ONLINE = 3
89 a8083063 Iustin Pop
90 a8083063 Iustin Pop
  STATUS_MAP = {
91 a8083063 Iustin Pop
    STATUS_UNKNOWN: "unknown",
92 a8083063 Iustin Pop
    STATUS_EXISTING: "existing",
93 a8083063 Iustin Pop
    STATUS_STANDBY: "ready for use",
94 a8083063 Iustin Pop
    STATUS_ONLINE: "online",
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
  def Assemble(self):
105 a8083063 Iustin Pop
    """Assemble the device from its components.
106 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

171 a8083063 Iustin Pop
    """
172 a8083063 Iustin Pop
    raise NotImplementedError
173 a8083063 Iustin Pop
174 f3e513ad Iustin Pop
  def Rename(self, new_id):
175 f3e513ad Iustin Pop
    """Rename this device.
176 f3e513ad Iustin Pop

177 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
178 f3e513ad Iustin Pop

179 f3e513ad Iustin Pop
    """
180 f3e513ad Iustin Pop
    raise NotImplementedError
181 f3e513ad Iustin Pop
182 a8083063 Iustin Pop
  def GetStatus(self):
183 a8083063 Iustin Pop
    """Return the status of the device.
184 a8083063 Iustin Pop

185 a8083063 Iustin Pop
    """
186 a8083063 Iustin Pop
    raise NotImplementedError
187 a8083063 Iustin Pop
188 a8083063 Iustin Pop
  def Open(self, force=False):
189 a8083063 Iustin Pop
    """Make the device ready for use.
190 a8083063 Iustin Pop

191 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
192 a8083063 Iustin Pop
    devices need this.
193 a8083063 Iustin Pop

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

197 a8083063 Iustin Pop
    """
198 a8083063 Iustin Pop
    raise NotImplementedError
199 a8083063 Iustin Pop
200 a8083063 Iustin Pop
  def Shutdown(self):
201 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
202 a8083063 Iustin Pop

203 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
204 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
205 a8083063 Iustin Pop
    assembled after this call.
206 a8083063 Iustin Pop

207 a8083063 Iustin Pop
    """
208 a8083063 Iustin Pop
    raise NotImplementedError
209 a8083063 Iustin Pop
210 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
211 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
212 a8083063 Iustin Pop

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

215 a8083063 Iustin Pop
    """
216 a8083063 Iustin Pop
    result = True
217 a8083063 Iustin Pop
    if self._children:
218 a8083063 Iustin Pop
      for child in self._children:
219 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
220 a8083063 Iustin Pop
    return result
221 a8083063 Iustin Pop
222 a8083063 Iustin Pop
  def GetSyncStatus(self):
223 a8083063 Iustin Pop
    """Returns the sync status of the device.
224 a8083063 Iustin Pop

225 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
226 a8083063 Iustin Pop
    status of the mirror.
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
    Returns:
229 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
230 0834c866 Iustin Pop

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

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

236 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
237 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
238 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
239 a8083063 Iustin Pop

240 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
241 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
242 0834c866 Iustin Pop
    return False (not degraded).
243 0834c866 Iustin Pop

244 a8083063 Iustin Pop
    """
245 0834c866 Iustin Pop
    return None, None, False, False
246 a8083063 Iustin Pop
247 a8083063 Iustin Pop
248 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
249 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
250 a8083063 Iustin Pop

251 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
252 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
253 a8083063 Iustin Pop
    children.
254 a8083063 Iustin Pop

255 a8083063 Iustin Pop
    """
256 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
257 a8083063 Iustin Pop
    if self._children:
258 a8083063 Iustin Pop
      for child in self._children:
259 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
260 a8083063 Iustin Pop
        if min_percent is None:
261 a8083063 Iustin Pop
          min_percent = c_percent
262 a8083063 Iustin Pop
        elif c_percent is not None:
263 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
264 a8083063 Iustin Pop
        if max_time is None:
265 a8083063 Iustin Pop
          max_time = c_time
266 a8083063 Iustin Pop
        elif c_time is not None:
267 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
268 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
269 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
270 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
273 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
274 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
275 a0c3fea1 Michael Hanselmann

276 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
277 a0c3fea1 Michael Hanselmann

278 a0c3fea1 Michael Hanselmann
    """
279 a0c3fea1 Michael Hanselmann
    for child in self._children:
280 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
281 a0c3fea1 Michael Hanselmann
282 a0c3fea1 Michael Hanselmann
283 a8083063 Iustin Pop
  def __repr__(self):
284 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
285 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
286 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
287 a8083063 Iustin Pop
288 a8083063 Iustin Pop
289 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
290 a8083063 Iustin Pop
  """Logical Volume block device.
291 a8083063 Iustin Pop

292 a8083063 Iustin Pop
  """
293 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
294 a8083063 Iustin Pop
    """Attaches to a LV device.
295 a8083063 Iustin Pop

296 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
297 a8083063 Iustin Pop

298 a8083063 Iustin Pop
    """
299 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
300 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
301 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
302 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
303 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
304 a8083063 Iustin Pop
    self.Attach()
305 a8083063 Iustin Pop
306 a8083063 Iustin Pop
  @classmethod
307 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
308 a8083063 Iustin Pop
    """Create a new logical volume.
309 a8083063 Iustin Pop

310 a8083063 Iustin Pop
    """
311 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
312 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
313 a8083063 Iustin Pop
    vg_name, lv_name = unique_id
314 a8083063 Iustin Pop
    pvs_info = cls.GetPVInfo(vg_name)
315 a8083063 Iustin Pop
    if not pvs_info:
316 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
317 3ecf6786 Iustin Pop
                                    vg_name)
318 a8083063 Iustin Pop
    pvs_info.sort()
319 a8083063 Iustin Pop
    pvs_info.reverse()
320 5b7b5d49 Guido Trotter
321 5b7b5d49 Guido Trotter
    pvlist = [ pv[1] for pv in pvs_info ]
322 5b7b5d49 Guido Trotter
    free_size = sum([ pv[0] for pv in pvs_info ])
323 5b7b5d49 Guido Trotter
324 5b7b5d49 Guido Trotter
    # The size constraint should have been checked from the master before
325 5b7b5d49 Guido Trotter
    # calling the create function.
326 a8083063 Iustin Pop
    if free_size < size:
327 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
328 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
329 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-n%s" % lv_name,
330 5b7b5d49 Guido Trotter
                           vg_name] + pvlist)
331 a8083063 Iustin Pop
    if result.failed:
332 a8083063 Iustin Pop
      raise errors.BlockDeviceError(result.fail_reason)
333 a8083063 Iustin Pop
    return LogicalVolume(unique_id, children)
334 a8083063 Iustin Pop
335 a8083063 Iustin Pop
  @staticmethod
336 a8083063 Iustin Pop
  def GetPVInfo(vg_name):
337 a8083063 Iustin Pop
    """Get the free space info for PVs in a volume group.
338 a8083063 Iustin Pop

339 a8083063 Iustin Pop
    Args:
340 a8083063 Iustin Pop
      vg_name: the volume group name
341 a8083063 Iustin Pop

342 a8083063 Iustin Pop
    Returns:
343 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
344 098c0958 Michael Hanselmann

345 a8083063 Iustin Pop
    """
346 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
347 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
348 a8083063 Iustin Pop
               "--separator=:"]
349 a8083063 Iustin Pop
    result = utils.RunCmd(command)
350 a8083063 Iustin Pop
    if result.failed:
351 a8083063 Iustin Pop
      logger.Error("Can't get the PV information: %s" % result.fail_reason)
352 a8083063 Iustin Pop
      return None
353 a8083063 Iustin Pop
    data = []
354 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
355 a8083063 Iustin Pop
      fields = line.strip().split(':')
356 a8083063 Iustin Pop
      if len(fields) != 4:
357 a8083063 Iustin Pop
        logger.Error("Can't parse pvs output: line '%s'" % line)
358 a8083063 Iustin Pop
        return None
359 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
360 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
361 a8083063 Iustin Pop
        continue
362 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
363 a8083063 Iustin Pop
364 a8083063 Iustin Pop
    return data
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
  def Remove(self):
367 a8083063 Iustin Pop
    """Remove this logical volume.
368 a8083063 Iustin Pop

369 a8083063 Iustin Pop
    """
370 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
371 a8083063 Iustin Pop
      # the LV does not exist
372 a8083063 Iustin Pop
      return True
373 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
374 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
375 a8083063 Iustin Pop
    if result.failed:
376 a8083063 Iustin Pop
      logger.Error("Can't lvremove: %s" % result.fail_reason)
377 a8083063 Iustin Pop
378 a8083063 Iustin Pop
    return not result.failed
379 a8083063 Iustin Pop
380 f3e513ad Iustin Pop
  def Rename(self, new_id):
381 f3e513ad Iustin Pop
    """Rename this logical volume.
382 f3e513ad Iustin Pop

383 f3e513ad Iustin Pop
    """
384 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
385 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
386 f3e513ad Iustin Pop
    new_vg, new_name = new_id
387 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
388 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
389 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
390 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
391 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
392 f3e513ad Iustin Pop
    if result.failed:
393 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
394 f3e513ad Iustin Pop
                                    result.output)
395 be345db0 Iustin Pop
    self._lv_name = new_name
396 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
397 be345db0 Iustin Pop
398 a8083063 Iustin Pop
  def Attach(self):
399 a8083063 Iustin Pop
    """Attach to an existing LV.
400 a8083063 Iustin Pop

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

405 a8083063 Iustin Pop
    """
406 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
407 a8083063 Iustin Pop
    if result.failed:
408 fcb1f331 Michael Hanselmann
      logger.Error("Can't find LV %s: %s, %s" %
409 fcb1f331 Michael Hanselmann
                   (self.dev_path, result.fail_reason, result.output))
410 a8083063 Iustin Pop
      return False
411 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
412 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
413 a8083063 Iustin Pop
      match_result = match.match(line)
414 a8083063 Iustin Pop
      if match_result:
415 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
416 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
417 a8083063 Iustin Pop
        return True
418 a8083063 Iustin Pop
    return False
419 a8083063 Iustin Pop
420 a8083063 Iustin Pop
  def Assemble(self):
421 a8083063 Iustin Pop
    """Assemble the device.
422 a8083063 Iustin Pop

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

427 a8083063 Iustin Pop
    """
428 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
429 5574047a Iustin Pop
    if result.failed:
430 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
431 5574047a Iustin Pop
    return not result.failed
432 a8083063 Iustin Pop
433 a8083063 Iustin Pop
  def Shutdown(self):
434 a8083063 Iustin Pop
    """Shutdown the device.
435 a8083063 Iustin Pop

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

439 a8083063 Iustin Pop
    """
440 a8083063 Iustin Pop
    return True
441 a8083063 Iustin Pop
442 a8083063 Iustin Pop
  def GetStatus(self):
443 a8083063 Iustin Pop
    """Return the status of the device.
444 a8083063 Iustin Pop

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

449 a8083063 Iustin Pop
    """
450 a8083063 Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
451 a8083063 Iustin Pop
    if result.failed:
452 a8083063 Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
453 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
454 a8083063 Iustin Pop
    out = result.stdout.strip()
455 a8083063 Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
456 a8083063 Iustin Pop
    if len(out) != 6:
457 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
458 a8083063 Iustin Pop
    #writable = (out[1] == "w")
459 a8083063 Iustin Pop
    active = (out[4] == "a")
460 a8083063 Iustin Pop
    online = (out[5] == "o")
461 a8083063 Iustin Pop
    if online:
462 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
463 a8083063 Iustin Pop
    elif active:
464 a8083063 Iustin Pop
      retval = self.STATUS_STANDBY
465 a8083063 Iustin Pop
    else:
466 a8083063 Iustin Pop
      retval = self.STATUS_EXISTING
467 a8083063 Iustin Pop
468 a8083063 Iustin Pop
    return retval
469 a8083063 Iustin Pop
470 9db6dbce Iustin Pop
  def GetSyncStatus(self):
471 9db6dbce Iustin Pop
    """Returns the sync status of the device.
472 9db6dbce Iustin Pop

473 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
474 9db6dbce Iustin Pop
    status of the mirror.
475 9db6dbce Iustin Pop

476 9db6dbce Iustin Pop
    Returns:
477 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
478 9db6dbce Iustin Pop

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

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

490 9db6dbce Iustin Pop
    """
491 9db6dbce Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
492 9db6dbce Iustin Pop
    if result.failed:
493 9db6dbce Iustin Pop
      logger.Error("Can't display lv: %s" % result.fail_reason)
494 0834c866 Iustin Pop
      return None, None, True, True
495 9db6dbce Iustin Pop
    out = result.stdout.strip()
496 9db6dbce Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
497 9db6dbce Iustin Pop
    if len(out) != 6:
498 0834c866 Iustin Pop
      logger.Debug("Error in lvs output: attrs=%s, len != 6" % out)
499 0834c866 Iustin Pop
      return None, None, True, True
500 0834c866 Iustin Pop
    ldisk = out[0] == 'v' # virtual volume, i.e. doesn't have
501 0834c866 Iustin Pop
                          # backing storage
502 0834c866 Iustin Pop
    return None, None, ldisk, ldisk
503 9db6dbce Iustin Pop
504 a8083063 Iustin Pop
  def Open(self, force=False):
505 a8083063 Iustin Pop
    """Make the device ready for I/O.
506 a8083063 Iustin Pop

507 a8083063 Iustin Pop
    This is a no-op for the LV device type.
508 a8083063 Iustin Pop

509 a8083063 Iustin Pop
    """
510 fdbd668d Iustin Pop
    pass
511 a8083063 Iustin Pop
512 a8083063 Iustin Pop
  def Close(self):
513 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
514 a8083063 Iustin Pop

515 a8083063 Iustin Pop
    This is a no-op for the LV device type.
516 a8083063 Iustin Pop

517 a8083063 Iustin Pop
    """
518 fdbd668d Iustin Pop
    pass
519 a8083063 Iustin Pop
520 a8083063 Iustin Pop
  def Snapshot(self, size):
521 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
522 a8083063 Iustin Pop

523 a8083063 Iustin Pop
    """
524 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
525 a8083063 Iustin Pop
526 a8083063 Iustin Pop
    # remove existing snapshot if found
527 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
528 a8083063 Iustin Pop
    snap.Remove()
529 a8083063 Iustin Pop
530 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
531 a8083063 Iustin Pop
    if not pvs_info:
532 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
533 3ecf6786 Iustin Pop
                                    self._vg_name)
534 a8083063 Iustin Pop
    pvs_info.sort()
535 a8083063 Iustin Pop
    pvs_info.reverse()
536 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
537 a8083063 Iustin Pop
    if free_size < size:
538 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
539 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
540 a8083063 Iustin Pop
541 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
542 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
543 a8083063 Iustin Pop
    if result.failed:
544 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s" %
545 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
546 a8083063 Iustin Pop
547 a8083063 Iustin Pop
    return snap_name
548 a8083063 Iustin Pop
549 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
550 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
551 a0c3fea1 Michael Hanselmann

552 a0c3fea1 Michael Hanselmann
    """
553 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
554 a0c3fea1 Michael Hanselmann
555 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
556 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
557 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
558 a0c3fea1 Michael Hanselmann
559 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
560 a0c3fea1 Michael Hanselmann
    text = text[:128]
561 a0c3fea1 Michael Hanselmann
562 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
563 a0c3fea1 Michael Hanselmann
                           self.dev_path])
564 a0c3fea1 Michael Hanselmann
    if result.failed:
565 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s" %
566 3ecf6786 Iustin Pop
                                    (result.cmd, result.fail_reason))
567 a0c3fea1 Michael Hanselmann
568 a0c3fea1 Michael Hanselmann
569 a8083063 Iustin Pop
class MDRaid1(BlockDev):
570 a8083063 Iustin Pop
  """raid1 device implemented via md.
571 a8083063 Iustin Pop

572 a8083063 Iustin Pop
  """
573 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
574 a8083063 Iustin Pop
    super(MDRaid1, self).__init__(unique_id, children)
575 a8083063 Iustin Pop
    self.major = 9
576 a8083063 Iustin Pop
    self.Attach()
577 a8083063 Iustin Pop
578 a8083063 Iustin Pop
  def Attach(self):
579 a8083063 Iustin Pop
    """Find an array which matches our config and attach to it.
580 a8083063 Iustin Pop

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

583 a8083063 Iustin Pop
    """
584 a8083063 Iustin Pop
    minor = self._FindMDByUUID(self.unique_id)
585 a8083063 Iustin Pop
    if minor is not None:
586 a8083063 Iustin Pop
      self._SetFromMinor(minor)
587 a8083063 Iustin Pop
    else:
588 a8083063 Iustin Pop
      self.minor = None
589 a8083063 Iustin Pop
      self.dev_path = None
590 a8083063 Iustin Pop
591 a8083063 Iustin Pop
    return (minor is not None)
592 a8083063 Iustin Pop
593 a8083063 Iustin Pop
  @staticmethod
594 a8083063 Iustin Pop
  def _GetUsedDevs():
595 a8083063 Iustin Pop
    """Compute the list of in-use MD devices.
596 a8083063 Iustin Pop

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

600 a8083063 Iustin Pop
    """
601 a8083063 Iustin Pop
    mdstat = open("/proc/mdstat", "r")
602 a8083063 Iustin Pop
    data = mdstat.readlines()
603 a8083063 Iustin Pop
    mdstat.close()
604 a8083063 Iustin Pop
605 a8083063 Iustin Pop
    used_md = {}
606 a8083063 Iustin Pop
    valid_line = re.compile("^md([0-9]+) : .*$")
607 a8083063 Iustin Pop
    for line in data:
608 a8083063 Iustin Pop
      match = valid_line.match(line)
609 a8083063 Iustin Pop
      if match:
610 a8083063 Iustin Pop
        md_no = int(match.group(1))
611 a8083063 Iustin Pop
        used_md[md_no] = line
612 a8083063 Iustin Pop
613 a8083063 Iustin Pop
    return used_md
614 a8083063 Iustin Pop
615 a8083063 Iustin Pop
  @staticmethod
616 a8083063 Iustin Pop
  def _GetDevInfo(minor):
617 a8083063 Iustin Pop
    """Get info about a MD device.
618 a8083063 Iustin Pop

619 a8083063 Iustin Pop
    Currently only uuid is returned.
620 a8083063 Iustin Pop

621 a8083063 Iustin Pop
    """
622 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-D", "/dev/md%d" % minor])
623 a8083063 Iustin Pop
    if result.failed:
624 a8083063 Iustin Pop
      logger.Error("Can't display md: %s" % result.fail_reason)
625 a8083063 Iustin Pop
      return None
626 a8083063 Iustin Pop
    retval = {}
627 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
628 a8083063 Iustin Pop
      line = line.strip()
629 a8083063 Iustin Pop
      kv = line.split(" : ", 1)
630 a8083063 Iustin Pop
      if kv:
631 a8083063 Iustin Pop
        if kv[0] == "UUID":
632 8d519422 Iustin Pop
          retval["uuid"] = kv[1].split()[0]
633 a8083063 Iustin Pop
        elif kv[0] == "State":
634 a8083063 Iustin Pop
          retval["state"] = kv[1].split(", ")
635 a8083063 Iustin Pop
    return retval
636 a8083063 Iustin Pop
637 a8083063 Iustin Pop
  @staticmethod
638 a8083063 Iustin Pop
  def _FindUnusedMinor():
639 a8083063 Iustin Pop
    """Compute an unused MD minor.
640 a8083063 Iustin Pop

641 a8083063 Iustin Pop
    This code assumes that there are 256 minors only.
642 a8083063 Iustin Pop

643 a8083063 Iustin Pop
    """
644 a8083063 Iustin Pop
    used_md = MDRaid1._GetUsedDevs()
645 a8083063 Iustin Pop
    i = 0
646 a8083063 Iustin Pop
    while i < 256:
647 a8083063 Iustin Pop
      if i not in used_md:
648 a8083063 Iustin Pop
        break
649 a8083063 Iustin Pop
      i += 1
650 a8083063 Iustin Pop
    if i == 256:
651 a8083063 Iustin Pop
      logger.Error("Critical: Out of md minor numbers.")
652 0caf6485 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free MD minor")
653 a8083063 Iustin Pop
    return i
654 a8083063 Iustin Pop
655 a8083063 Iustin Pop
  @classmethod
656 a8083063 Iustin Pop
  def _FindMDByUUID(cls, uuid):
657 a8083063 Iustin Pop
    """Find the minor of an MD array with a given UUID.
658 a8083063 Iustin Pop

659 a8083063 Iustin Pop
    """
660 a8083063 Iustin Pop
    md_list = cls._GetUsedDevs()
661 a8083063 Iustin Pop
    for minor in md_list:
662 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
663 a8083063 Iustin Pop
      if info and info["uuid"] == uuid:
664 a8083063 Iustin Pop
        return minor
665 a8083063 Iustin Pop
    return None
666 a8083063 Iustin Pop
667 1a87dca7 Iustin Pop
  @staticmethod
668 1a87dca7 Iustin Pop
  def _ZeroSuperblock(dev_path):
669 1a87dca7 Iustin Pop
    """Zero the possible locations for an MD superblock.
670 1a87dca7 Iustin Pop

671 1a87dca7 Iustin Pop
    The zero-ing can't be done via ``mdadm --zero-superblock`` as that
672 1a87dca7 Iustin Pop
    fails in versions 2.x with the same error code as non-writable
673 1a87dca7 Iustin Pop
    device.
674 1a87dca7 Iustin Pop

675 1a87dca7 Iustin Pop
    The superblocks are located at (negative values are relative to
676 1a87dca7 Iustin Pop
    the end of the block device):
677 1a87dca7 Iustin Pop
      - -128k to end for version 0.90 superblock
678 1a87dca7 Iustin Pop
      - -8k to -12k for version 1.0 superblock (included in the above)
679 1a87dca7 Iustin Pop
      - 0k to 4k for version 1.1 superblock
680 1a87dca7 Iustin Pop
      - 4k to 8k for version 1.2 superblock
681 1a87dca7 Iustin Pop

682 1a87dca7 Iustin Pop
    To cover all situations, the zero-ing will be:
683 1a87dca7 Iustin Pop
      - 0k to 128k
684 1a87dca7 Iustin Pop
      - -128k to end
685 1a87dca7 Iustin Pop

686 1a87dca7 Iustin Pop
    As such, the minimum device size must be 128k, otherwise we'll get
687 1a87dca7 Iustin Pop
    I/O errors.
688 1a87dca7 Iustin Pop

689 1a87dca7 Iustin Pop
    Note that this function depends on the fact that one can open,
690 1a87dca7 Iustin Pop
    read and write block devices normally.
691 1a87dca7 Iustin Pop

692 1a87dca7 Iustin Pop
    """
693 1a87dca7 Iustin Pop
    overwrite_size = 128 * 1024
694 1a87dca7 Iustin Pop
    empty_buf = '\0' * overwrite_size
695 1a87dca7 Iustin Pop
    fd = open(dev_path, "r+")
696 1a87dca7 Iustin Pop
    try:
697 1a87dca7 Iustin Pop
      fd.seek(0, 0)
698 1a87dca7 Iustin Pop
      p1 = fd.tell()
699 1a87dca7 Iustin Pop
      fd.write(empty_buf)
700 1a87dca7 Iustin Pop
      p2 = fd.tell()
701 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
702 1a87dca7 Iustin Pop
      fd.seek(-overwrite_size, 2)
703 1a87dca7 Iustin Pop
      p1 = fd.tell()
704 1a87dca7 Iustin Pop
      fd.write(empty_buf)
705 1a87dca7 Iustin Pop
      p2 = fd.tell()
706 1a87dca7 Iustin Pop
      logger.Debug("Zeroed %s from %d to %d" % (dev_path, p1, p2))
707 1a87dca7 Iustin Pop
    finally:
708 1a87dca7 Iustin Pop
      fd.close()
709 1a87dca7 Iustin Pop
710 a8083063 Iustin Pop
  @classmethod
711 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
712 a8083063 Iustin Pop
    """Create a new MD raid1 array.
713 a8083063 Iustin Pop

714 a8083063 Iustin Pop
    """
715 a8083063 Iustin Pop
    if not isinstance(children, (tuple, list)):
716 a8083063 Iustin Pop
      raise ValueError("Invalid setup data for MDRaid1 dev: %s" %
717 a8083063 Iustin Pop
                       str(children))
718 a8083063 Iustin Pop
    for i in children:
719 a8083063 Iustin Pop
      if not isinstance(i, BlockDev):
720 a8083063 Iustin Pop
        raise ValueError("Invalid member in MDRaid1 dev: %s" % type(i))
721 a8083063 Iustin Pop
    for i in children:
722 1a87dca7 Iustin Pop
      try:
723 1a87dca7 Iustin Pop
        cls._ZeroSuperblock(i.dev_path)
724 1a87dca7 Iustin Pop
      except EnvironmentError, err:
725 1a87dca7 Iustin Pop
        logger.Error("Can't zero superblock for %s: %s" %
726 1a87dca7 Iustin Pop
                     (i.dev_path, str(err)))
727 a8083063 Iustin Pop
        return None
728 a8083063 Iustin Pop
    minor = cls._FindUnusedMinor()
729 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--create", "/dev/md%d" % minor,
730 a8083063 Iustin Pop
                           "--auto=yes", "--force", "-l1",
731 a8083063 Iustin Pop
                           "-n%d" % len(children)] +
732 a8083063 Iustin Pop
                          [dev.dev_path for dev in children])
733 a8083063 Iustin Pop
734 a8083063 Iustin Pop
    if result.failed:
735 1a87dca7 Iustin Pop
      logger.Error("Can't create md: %s: %s" % (result.fail_reason,
736 1a87dca7 Iustin Pop
                                                result.output))
737 a8083063 Iustin Pop
      return None
738 a8083063 Iustin Pop
    info = cls._GetDevInfo(minor)
739 a8083063 Iustin Pop
    if not info or not "uuid" in info:
740 a8083063 Iustin Pop
      logger.Error("Wrong information returned from mdadm -D: %s" % str(info))
741 a8083063 Iustin Pop
      return None
742 a8083063 Iustin Pop
    return MDRaid1(info["uuid"], children)
743 a8083063 Iustin Pop
744 a8083063 Iustin Pop
  def Remove(self):
745 a8083063 Iustin Pop
    """Stub remove function for MD RAID 1 arrays.
746 a8083063 Iustin Pop

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

749 a8083063 Iustin Pop
    """
750 a8083063 Iustin Pop
    #TODO: maybe zero superblock on child devices?
751 a8083063 Iustin Pop
    return self.Shutdown()
752 a8083063 Iustin Pop
753 f3e513ad Iustin Pop
  def Rename(self, new_id):
754 f3e513ad Iustin Pop
    """Rename a device.
755 f3e513ad Iustin Pop

756 f3e513ad Iustin Pop
    This is not supported for md raid1 devices.
757 f3e513ad Iustin Pop

758 f3e513ad Iustin Pop
    """
759 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a md raid1 device")
760 a8083063 Iustin Pop
761 153d9724 Iustin Pop
  def AddChildren(self, devices):
762 153d9724 Iustin Pop
    """Add new member(s) to the md raid1.
763 a8083063 Iustin Pop

764 a8083063 Iustin Pop
    """
765 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
766 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
767 153d9724 Iustin Pop
768 153d9724 Iustin Pop
    args = ["mdadm", "-a", self.dev_path]
769 153d9724 Iustin Pop
    for dev in devices:
770 153d9724 Iustin Pop
      if dev.dev_path is None:
771 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Child '%s' is not initialised" % dev)
772 153d9724 Iustin Pop
      dev.Open()
773 153d9724 Iustin Pop
      args.append(dev.dev_path)
774 153d9724 Iustin Pop
    result = utils.RunCmd(args)
775 a8083063 Iustin Pop
    if result.failed:
776 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Failed to add new device to array: %s" %
777 3ecf6786 Iustin Pop
                                    result.output)
778 153d9724 Iustin Pop
    new_len = len(self._children) + len(devices)
779 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", self.dev_path, "-n", new_len])
780 a8083063 Iustin Pop
    if result.failed:
781 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't grow md array: %s" %
782 3ecf6786 Iustin Pop
                                    result.output)
783 153d9724 Iustin Pop
    self._children.extend(devices)
784 a8083063 Iustin Pop
785 153d9724 Iustin Pop
  def RemoveChildren(self, devices):
786 153d9724 Iustin Pop
    """Remove member(s) from the md raid1.
787 a8083063 Iustin Pop

788 a8083063 Iustin Pop
    """
789 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
790 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device")
791 153d9724 Iustin Pop
    new_len = len(self._children) - len(devices)
792 153d9724 Iustin Pop
    if new_len < 1:
793 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Can't reduce to less than one child")
794 153d9724 Iustin Pop
    args = ["mdadm", "-f", self.dev_path]
795 153d9724 Iustin Pop
    orig_devs = []
796 153d9724 Iustin Pop
    for dev in devices:
797 e739bd57 Iustin Pop
      args.append(dev)
798 153d9724 Iustin Pop
      for c in self._children:
799 e739bd57 Iustin Pop
        if c.dev_path == dev:
800 153d9724 Iustin Pop
          orig_devs.append(c)
801 153d9724 Iustin Pop
          break
802 153d9724 Iustin Pop
      else:
803 153d9724 Iustin Pop
        raise errors.BlockDeviceError("Can't find device '%s' for removal" %
804 153d9724 Iustin Pop
                                      dev)
805 153d9724 Iustin Pop
    result = utils.RunCmd(args)
806 a8083063 Iustin Pop
    if result.failed:
807 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to mark device(s) as failed: %s" %
808 3ecf6786 Iustin Pop
                                    result.output)
809 a8083063 Iustin Pop
810 a8083063 Iustin Pop
    # it seems here we need a short delay for MD to update its
811 a8083063 Iustin Pop
    # superblocks
812 a8083063 Iustin Pop
    time.sleep(0.5)
813 153d9724 Iustin Pop
    args[1] = "-r"
814 153d9724 Iustin Pop
    result = utils.RunCmd(args)
815 a8083063 Iustin Pop
    if result.failed:
816 153d9724 Iustin Pop
      raise errors.BlockDeviceError("Failed to remove device(s) from array:"
817 153d9724 Iustin Pop
                                    " %s" % result.output)
818 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--grow", "--force", self.dev_path,
819 a8083063 Iustin Pop
                           "-n", new_len])
820 a8083063 Iustin Pop
    if result.failed:
821 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't shrink md array: %s" %
822 3ecf6786 Iustin Pop
                                    result.output)
823 153d9724 Iustin Pop
    for dev in orig_devs:
824 153d9724 Iustin Pop
      self._children.remove(dev)
825 a8083063 Iustin Pop
826 a8083063 Iustin Pop
  def GetStatus(self):
827 a8083063 Iustin Pop
    """Return the status of the device.
828 a8083063 Iustin Pop

829 a8083063 Iustin Pop
    """
830 a8083063 Iustin Pop
    self.Attach()
831 a8083063 Iustin Pop
    if self.minor is None:
832 a8083063 Iustin Pop
      retval = self.STATUS_UNKNOWN
833 a8083063 Iustin Pop
    else:
834 a8083063 Iustin Pop
      retval = self.STATUS_ONLINE
835 a8083063 Iustin Pop
    return retval
836 a8083063 Iustin Pop
837 a8083063 Iustin Pop
  def _SetFromMinor(self, minor):
838 a8083063 Iustin Pop
    """Set our parameters based on the given minor.
839 a8083063 Iustin Pop

840 a8083063 Iustin Pop
    This sets our minor variable and our dev_path.
841 a8083063 Iustin Pop

842 a8083063 Iustin Pop
    """
843 a8083063 Iustin Pop
    self.minor = minor
844 a8083063 Iustin Pop
    self.dev_path = "/dev/md%d" % minor
845 a8083063 Iustin Pop
846 a8083063 Iustin Pop
  def Assemble(self):
847 a8083063 Iustin Pop
    """Assemble the MD device.
848 a8083063 Iustin Pop

849 a8083063 Iustin Pop
    At this point we should have:
850 a8083063 Iustin Pop
      - list of children devices
851 a8083063 Iustin Pop
      - uuid
852 a8083063 Iustin Pop

853 a8083063 Iustin Pop
    """
854 a8083063 Iustin Pop
    result = super(MDRaid1, self).Assemble()
855 a8083063 Iustin Pop
    if not result:
856 a8083063 Iustin Pop
      return result
857 a8083063 Iustin Pop
    md_list = self._GetUsedDevs()
858 a8083063 Iustin Pop
    for minor in md_list:
859 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
860 a8083063 Iustin Pop
      if info and info["uuid"] == self.unique_id:
861 a8083063 Iustin Pop
        self._SetFromMinor(minor)
862 a8083063 Iustin Pop
        logger.Info("MD array %s already started" % str(self))
863 a8083063 Iustin Pop
        return True
864 a8083063 Iustin Pop
    free_minor = self._FindUnusedMinor()
865 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "-A", "--auto=yes", "--uuid",
866 a8083063 Iustin Pop
                           self.unique_id, "/dev/md%d" % free_minor] +
867 a8083063 Iustin Pop
                          [bdev.dev_path for bdev in self._children])
868 a8083063 Iustin Pop
    if result.failed:
869 8d519422 Iustin Pop
      logger.Error("Can't assemble MD array: %s: %s" %
870 8d519422 Iustin Pop
                   (result.fail_reason, result.output))
871 a8083063 Iustin Pop
      self.minor = None
872 a8083063 Iustin Pop
    else:
873 a8083063 Iustin Pop
      self.minor = free_minor
874 a8083063 Iustin Pop
    return not result.failed
875 a8083063 Iustin Pop
876 a8083063 Iustin Pop
  def Shutdown(self):
877 a8083063 Iustin Pop
    """Tear down the MD array.
878 a8083063 Iustin Pop

879 a8083063 Iustin Pop
    This does a 'mdadm --stop' so after this command, the array is no
880 a8083063 Iustin Pop
    longer available.
881 a8083063 Iustin Pop

882 a8083063 Iustin Pop
    """
883 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
884 a8083063 Iustin Pop
      logger.Info("MD object not attached to a device")
885 a8083063 Iustin Pop
      return True
886 a8083063 Iustin Pop
887 a8083063 Iustin Pop
    result = utils.RunCmd(["mdadm", "--stop", "/dev/md%d" % self.minor])
888 a8083063 Iustin Pop
    if result.failed:
889 a8083063 Iustin Pop
      logger.Error("Can't stop MD array: %s" % result.fail_reason)
890 a8083063 Iustin Pop
      return False
891 a8083063 Iustin Pop
    self.minor = None
892 a8083063 Iustin Pop
    self.dev_path = None
893 a8083063 Iustin Pop
    return True
894 a8083063 Iustin Pop
895 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
896 a8083063 Iustin Pop
    """Set the maximum sync speed for the MD array.
897 a8083063 Iustin Pop

898 a8083063 Iustin Pop
    """
899 a8083063 Iustin Pop
    result = super(MDRaid1, self).SetSyncSpeed(kbytes)
900 a8083063 Iustin Pop
    if self.minor is None:
901 a8083063 Iustin Pop
      logger.Error("MD array not attached to a device")
902 a8083063 Iustin Pop
      return False
903 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_max" % self.minor, "w")
904 a8083063 Iustin Pop
    try:
905 a8083063 Iustin Pop
      f.write("%d" % kbytes)
906 a8083063 Iustin Pop
    finally:
907 a8083063 Iustin Pop
      f.close()
908 a8083063 Iustin Pop
    f = open("/sys/block/md%d/md/sync_speed_min" % self.minor, "w")
909 a8083063 Iustin Pop
    try:
910 a8083063 Iustin Pop
      f.write("%d" % (kbytes/2))
911 a8083063 Iustin Pop
    finally:
912 a8083063 Iustin Pop
      f.close()
913 a8083063 Iustin Pop
    return result
914 a8083063 Iustin Pop
915 a8083063 Iustin Pop
  def GetSyncStatus(self):
916 a8083063 Iustin Pop
    """Returns the sync status of the device.
917 a8083063 Iustin Pop

918 a8083063 Iustin Pop
    Returns:
919 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
920 a8083063 Iustin Pop

921 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
922 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
923 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
924 0834c866 Iustin Pop

925 0834c866 Iustin Pop
    The ldisk parameter is always true for MD devices.
926 a8083063 Iustin Pop

927 a8083063 Iustin Pop
    """
928 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
929 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
930 a8083063 Iustin Pop
    dev_info = self._GetDevInfo(self.minor)
931 a8083063 Iustin Pop
    is_clean = ("state" in dev_info and
932 a8083063 Iustin Pop
                len(dev_info["state"]) == 1 and
933 a8083063 Iustin Pop
                dev_info["state"][0] in ("clean", "active"))
934 a8083063 Iustin Pop
    sys_path = "/sys/block/md%s/md/" % self.minor
935 a8083063 Iustin Pop
    f = file(sys_path + "sync_action")
936 a8083063 Iustin Pop
    sync_status = f.readline().strip()
937 a8083063 Iustin Pop
    f.close()
938 a8083063 Iustin Pop
    if sync_status == "idle":
939 0834c866 Iustin Pop
      return None, None, not is_clean, False
940 a8083063 Iustin Pop
    f = file(sys_path + "sync_completed")
941 a8083063 Iustin Pop
    sync_completed = f.readline().strip().split(" / ")
942 a8083063 Iustin Pop
    f.close()
943 a8083063 Iustin Pop
    if len(sync_completed) != 2:
944 0834c866 Iustin Pop
      return 0, None, not is_clean, False
945 a8083063 Iustin Pop
    sync_done, sync_total = [float(i) for i in sync_completed]
946 a8083063 Iustin Pop
    sync_percent = 100.0*sync_done/sync_total
947 a8083063 Iustin Pop
    f = file(sys_path + "sync_speed")
948 a8083063 Iustin Pop
    sync_speed_k = int(f.readline().strip())
949 a8083063 Iustin Pop
    if sync_speed_k == 0:
950 a8083063 Iustin Pop
      time_est = None
951 a8083063 Iustin Pop
    else:
952 a8083063 Iustin Pop
      time_est = (sync_total - sync_done) / 2 / sync_speed_k
953 0834c866 Iustin Pop
    return sync_percent, time_est, not is_clean, False
954 a8083063 Iustin Pop
955 a8083063 Iustin Pop
  def Open(self, force=False):
956 a8083063 Iustin Pop
    """Make the device ready for I/O.
957 a8083063 Iustin Pop

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

961 a8083063 Iustin Pop
    """
962 fdbd668d Iustin Pop
    pass
963 a8083063 Iustin Pop
964 a8083063 Iustin Pop
  def Close(self):
965 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
966 a8083063 Iustin Pop

967 a8083063 Iustin Pop
    This is a no-op for the MDRaid1 device type, but see comment for
968 a8083063 Iustin Pop
    `Open()`.
969 a8083063 Iustin Pop

970 a8083063 Iustin Pop
    """
971 fdbd668d Iustin Pop
    pass
972 a8083063 Iustin Pop
973 a8083063 Iustin Pop
974 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
975 0f7f32d9 Iustin Pop
  """Base DRBD class.
976 a8083063 Iustin Pop

977 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
978 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
979 0f7f32d9 Iustin Pop

980 0f7f32d9 Iustin Pop
  """
981 0f7f32d9 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
982 c3f9340c Guido Trotter
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
983 c3f9340c Guido Trotter
984 770fe0f9 Iustin Pop
  _DRBD_MAJOR = 147
985 770fe0f9 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
986 770fe0f9 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
987 770fe0f9 Iustin Pop
  _ST_CONNECTED = "Connected"
988 0f7f32d9 Iustin Pop
989 0f7f32d9 Iustin Pop
  @staticmethod
990 0f7f32d9 Iustin Pop
  def _GetProcData():
991 0f7f32d9 Iustin Pop
    """Return data from /proc/drbd.
992 0f7f32d9 Iustin Pop

993 0f7f32d9 Iustin Pop
    """
994 0f7f32d9 Iustin Pop
    stat = open("/proc/drbd", "r")
995 0f7f32d9 Iustin Pop
    try:
996 0f7f32d9 Iustin Pop
      data = stat.read().splitlines()
997 0f7f32d9 Iustin Pop
    finally:
998 0f7f32d9 Iustin Pop
      stat.close()
999 0f7f32d9 Iustin Pop
    if not data:
1000 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
1001 0f7f32d9 Iustin Pop
    return data
1002 0f7f32d9 Iustin Pop
1003 5a47ad20 Iustin Pop
  @staticmethod
1004 5a47ad20 Iustin Pop
  def _MassageProcData(data):
1005 5a47ad20 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
1006 5a47ad20 Iustin Pop

1007 5a47ad20 Iustin Pop
    Returns:
1008 5a47ad20 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
1009 5a47ad20 Iustin Pop

1010 5a47ad20 Iustin Pop
    """
1011 5a47ad20 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
1012 5a47ad20 Iustin Pop
    results = {}
1013 5a47ad20 Iustin Pop
    old_minor = old_line = None
1014 5a47ad20 Iustin Pop
    for line in data:
1015 5a47ad20 Iustin Pop
      lresult = lmatch.match(line)
1016 5a47ad20 Iustin Pop
      if lresult is not None:
1017 5a47ad20 Iustin Pop
        if old_minor is not None:
1018 5a47ad20 Iustin Pop
          results[old_minor] = old_line
1019 5a47ad20 Iustin Pop
        old_minor = int(lresult.group(1))
1020 5a47ad20 Iustin Pop
        old_line = line
1021 5a47ad20 Iustin Pop
      else:
1022 5a47ad20 Iustin Pop
        if old_minor is not None:
1023 5a47ad20 Iustin Pop
          old_line += " " + line.strip()
1024 5a47ad20 Iustin Pop
    # add last line
1025 5a47ad20 Iustin Pop
    if old_minor is not None:
1026 5a47ad20 Iustin Pop
      results[old_minor] = old_line
1027 5a47ad20 Iustin Pop
    return results
1028 5a47ad20 Iustin Pop
1029 0f7f32d9 Iustin Pop
  @classmethod
1030 0f7f32d9 Iustin Pop
  def _GetVersion(cls):
1031 0f7f32d9 Iustin Pop
    """Return the DRBD version.
1032 0f7f32d9 Iustin Pop

1033 c3f9340c Guido Trotter
    This will return a dict with keys:
1034 c3f9340c Guido Trotter
      k_major,
1035 c3f9340c Guido Trotter
      k_minor,
1036 c3f9340c Guido Trotter
      k_point,
1037 c3f9340c Guido Trotter
      api,
1038 c3f9340c Guido Trotter
      proto,
1039 c3f9340c Guido Trotter
      proto2 (only on drbd > 8.2.X)
1040 0f7f32d9 Iustin Pop

1041 0f7f32d9 Iustin Pop
    """
1042 0f7f32d9 Iustin Pop
    proc_data = cls._GetProcData()
1043 0f7f32d9 Iustin Pop
    first_line = proc_data[0].strip()
1044 0f7f32d9 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
1045 0f7f32d9 Iustin Pop
    if not version:
1046 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
1047 0f7f32d9 Iustin Pop
                                    first_line)
1048 c3f9340c Guido Trotter
1049 c3f9340c Guido Trotter
    values = version.groups()
1050 c3f9340c Guido Trotter
    retval = {'k_major': int(values[0]),
1051 c3f9340c Guido Trotter
              'k_minor': int(values[1]),
1052 c3f9340c Guido Trotter
              'k_point': int(values[2]),
1053 c3f9340c Guido Trotter
              'api': int(values[3]),
1054 c3f9340c Guido Trotter
              'proto': int(values[4]),
1055 c3f9340c Guido Trotter
             }
1056 c3f9340c Guido Trotter
    if values[5] is not None:
1057 c3f9340c Guido Trotter
      retval['proto2'] = values[5]
1058 c3f9340c Guido Trotter
1059 c3f9340c Guido Trotter
    return retval
1060 0f7f32d9 Iustin Pop
1061 770fe0f9 Iustin Pop
  @staticmethod
1062 770fe0f9 Iustin Pop
  def _DevPath(minor):
1063 770fe0f9 Iustin Pop
    """Return the path to a drbd device for a given minor.
1064 770fe0f9 Iustin Pop

1065 770fe0f9 Iustin Pop
    """
1066 770fe0f9 Iustin Pop
    return "/dev/drbd%d" % minor
1067 770fe0f9 Iustin Pop
1068 770fe0f9 Iustin Pop
  @classmethod
1069 770fe0f9 Iustin Pop
  def _GetUsedDevs(cls):
1070 770fe0f9 Iustin Pop
    """Compute the list of used DRBD devices.
1071 770fe0f9 Iustin Pop

1072 770fe0f9 Iustin Pop
    """
1073 770fe0f9 Iustin Pop
    data = cls._GetProcData()
1074 770fe0f9 Iustin Pop
1075 770fe0f9 Iustin Pop
    used_devs = {}
1076 770fe0f9 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
1077 770fe0f9 Iustin Pop
    for line in data:
1078 770fe0f9 Iustin Pop
      match = valid_line.match(line)
1079 770fe0f9 Iustin Pop
      if not match:
1080 770fe0f9 Iustin Pop
        continue
1081 770fe0f9 Iustin Pop
      minor = int(match.group(1))
1082 770fe0f9 Iustin Pop
      state = match.group(2)
1083 770fe0f9 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
1084 770fe0f9 Iustin Pop
        continue
1085 770fe0f9 Iustin Pop
      used_devs[minor] = state, line
1086 770fe0f9 Iustin Pop
1087 770fe0f9 Iustin Pop
    return used_devs
1088 770fe0f9 Iustin Pop
1089 5a47ad20 Iustin Pop
  def _SetFromMinor(self, minor):
1090 5a47ad20 Iustin Pop
    """Set our parameters based on the given minor.
1091 5a47ad20 Iustin Pop

1092 5a47ad20 Iustin Pop
    This sets our minor variable and our dev_path.
1093 5a47ad20 Iustin Pop

1094 5a47ad20 Iustin Pop
    """
1095 5a47ad20 Iustin Pop
    if minor is None:
1096 5a47ad20 Iustin Pop
      self.minor = self.dev_path = None
1097 5a47ad20 Iustin Pop
    else:
1098 5a47ad20 Iustin Pop
      self.minor = minor
1099 5a47ad20 Iustin Pop
      self.dev_path = self._DevPath(minor)
1100 5a47ad20 Iustin Pop
1101 ae26a287 Iustin Pop
  @staticmethod
1102 ae26a287 Iustin Pop
  def _CheckMetaSize(meta_device):
1103 ae26a287 Iustin Pop
    """Check if the given meta device looks like a valid one.
1104 ae26a287 Iustin Pop

1105 ae26a287 Iustin Pop
    This currently only check the size, which must be around
1106 ae26a287 Iustin Pop
    128MiB.
1107 ae26a287 Iustin Pop

1108 ae26a287 Iustin Pop
    """
1109 ae26a287 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
1110 ae26a287 Iustin Pop
    if result.failed:
1111 ae26a287 Iustin Pop
      logger.Error("Failed to get device size: %s" % result.fail_reason)
1112 ae26a287 Iustin Pop
      return False
1113 ae26a287 Iustin Pop
    try:
1114 ae26a287 Iustin Pop
      sectors = int(result.stdout)
1115 ae26a287 Iustin Pop
    except ValueError:
1116 ae26a287 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
1117 ae26a287 Iustin Pop
      return False
1118 ae26a287 Iustin Pop
    bytes = sectors * 512
1119 ae26a287 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
1120 ae26a287 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
1121 ae26a287 Iustin Pop
      return False
1122 ae26a287 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
1123 ae26a287 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
1124 ae26a287 Iustin Pop
      return False
1125 ae26a287 Iustin Pop
    return True
1126 ae26a287 Iustin Pop
1127 f3e513ad Iustin Pop
  def Rename(self, new_id):
1128 f3e513ad Iustin Pop
    """Rename a device.
1129 f3e513ad Iustin Pop

1130 f3e513ad Iustin Pop
    This is not supported for drbd devices.
1131 f3e513ad Iustin Pop

1132 f3e513ad Iustin Pop
    """
1133 f3e513ad Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
1134 f3e513ad Iustin Pop
1135 0f7f32d9 Iustin Pop
1136 0f7f32d9 Iustin Pop
class DRBDev(BaseDRBD):
1137 a8083063 Iustin Pop
  """DRBD block device.
1138 a8083063 Iustin Pop

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

1143 a8083063 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1144 a8083063 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1145 a8083063 Iustin Pop
  data device and the meta_device. The meta device is checked for
1146 a8083063 Iustin Pop
  valid size and is zeroed on create.
1147 a8083063 Iustin Pop

1148 a8083063 Iustin Pop
  """
1149 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
1150 a8083063 Iustin Pop
    super(DRBDev, self).__init__(unique_id, children)
1151 a8083063 Iustin Pop
    self.major = self._DRBD_MAJOR
1152 c3f9340c Guido Trotter
    version = self._GetVersion()
1153 c3f9340c Guido Trotter
    if version['k_major'] != 0 and version['k_minor'] != 7:
1154 0f7f32d9 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1155 0f7f32d9 Iustin Pop
                                    " requested ganeti usage: kernel is"
1156 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 0.7" %
1157 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
1158 a8083063 Iustin Pop
    if len(children) != 2:
1159 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1160 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1161 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1162 a8083063 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1163 a8083063 Iustin Pop
    self.Attach()
1164 a8083063 Iustin Pop
1165 a8083063 Iustin Pop
  @classmethod
1166 a8083063 Iustin Pop
  def _FindUnusedMinor(cls):
1167 a8083063 Iustin Pop
    """Find an unused DRBD device.
1168 a8083063 Iustin Pop

1169 a8083063 Iustin Pop
    """
1170 a8083063 Iustin Pop
    data = cls._GetProcData()
1171 a8083063 Iustin Pop
1172 a8083063 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1173 a8083063 Iustin Pop
    for line in data:
1174 a8083063 Iustin Pop
      match = valid_line.match(line)
1175 a8083063 Iustin Pop
      if match:
1176 a8083063 Iustin Pop
        return int(match.group(1))
1177 a8083063 Iustin Pop
    logger.Error("Error: no free drbd minors!")
1178 0caf6485 Iustin Pop
    raise errors.BlockDeviceError("Can't find a free DRBD minor")
1179 a8083063 Iustin Pop
1180 a8083063 Iustin Pop
  @classmethod
1181 a8083063 Iustin Pop
  def _GetDevInfo(cls, minor):
1182 a8083063 Iustin Pop
    """Get details about a given DRBD minor.
1183 a8083063 Iustin Pop

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

1187 a8083063 Iustin Pop
    """
1188 a8083063 Iustin Pop
    data = {}
1189 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1190 a8083063 Iustin Pop
    if result.failed:
1191 a8083063 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1192 a8083063 Iustin Pop
      return data
1193 a8083063 Iustin Pop
    out = result.stdout
1194 a8083063 Iustin Pop
    if out == "Not configured\n":
1195 a8083063 Iustin Pop
      return data
1196 a8083063 Iustin Pop
    for line in out.splitlines():
1197 a8083063 Iustin Pop
      if "local_dev" not in data:
1198 a8083063 Iustin Pop
        match = re.match("^Lower device: ([0-9]+):([0-9]+) .*$", line)
1199 a8083063 Iustin Pop
        if match:
1200 a8083063 Iustin Pop
          data["local_dev"] = (int(match.group(1)), int(match.group(2)))
1201 a8083063 Iustin Pop
          continue
1202 a8083063 Iustin Pop
      if "meta_dev" not in data:
1203 a8083063 Iustin Pop
        match = re.match("^Meta device: (([0-9]+):([0-9]+)|internal).*$", line)
1204 a8083063 Iustin Pop
        if match:
1205 a8083063 Iustin Pop
          if match.group(2) is not None and match.group(3) is not None:
1206 a8083063 Iustin Pop
            # matched on the major/minor
1207 a8083063 Iustin Pop
            data["meta_dev"] = (int(match.group(2)), int(match.group(3)))
1208 a8083063 Iustin Pop
          else:
1209 a8083063 Iustin Pop
            # matched on the "internal" string
1210 a8083063 Iustin Pop
            data["meta_dev"] = match.group(1)
1211 a8083063 Iustin Pop
            # in this case, no meta_index is in the output
1212 a8083063 Iustin Pop
            data["meta_index"] = -1
1213 a8083063 Iustin Pop
          continue
1214 a8083063 Iustin Pop
      if "meta_index" not in data:
1215 a8083063 Iustin Pop
        match = re.match("^Meta index: ([0-9]+).*$", line)
1216 a8083063 Iustin Pop
        if match:
1217 a8083063 Iustin Pop
          data["meta_index"] = int(match.group(1))
1218 a8083063 Iustin Pop
          continue
1219 a8083063 Iustin Pop
      if "local_addr" not in data:
1220 a8083063 Iustin Pop
        match = re.match("^Local address: ([0-9.]+):([0-9]+)$", line)
1221 a8083063 Iustin Pop
        if match:
1222 a8083063 Iustin Pop
          data["local_addr"] = (match.group(1), int(match.group(2)))
1223 a8083063 Iustin Pop
          continue
1224 a8083063 Iustin Pop
      if "remote_addr" not in data:
1225 a8083063 Iustin Pop
        match = re.match("^Remote address: ([0-9.]+):([0-9]+)$", line)
1226 a8083063 Iustin Pop
        if match:
1227 a8083063 Iustin Pop
          data["remote_addr"] = (match.group(1), int(match.group(2)))
1228 a8083063 Iustin Pop
          continue
1229 a8083063 Iustin Pop
    return data
1230 a8083063 Iustin Pop
1231 a8083063 Iustin Pop
  def _MatchesLocal(self, info):
1232 a8083063 Iustin Pop
    """Test if our local config matches with an existing device.
1233 a8083063 Iustin Pop

1234 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1235 a8083063 Iustin Pop
    method tests if our local backing device is the same as the one in
1236 a8083063 Iustin Pop
    the info parameter, in effect testing if we look like the given
1237 a8083063 Iustin Pop
    device.
1238 a8083063 Iustin Pop

1239 a8083063 Iustin Pop
    """
1240 a8083063 Iustin Pop
    if not ("local_dev" in info and "meta_dev" in info and
1241 a8083063 Iustin Pop
            "meta_index" in info):
1242 a8083063 Iustin Pop
      return False
1243 a8083063 Iustin Pop
1244 a8083063 Iustin Pop
    backend = self._children[0]
1245 a8083063 Iustin Pop
    if backend is not None:
1246 a8083063 Iustin Pop
      retval = (info["local_dev"] == (backend.major, backend.minor))
1247 a8083063 Iustin Pop
    else:
1248 a8083063 Iustin Pop
      retval = (info["local_dev"] == (0, 0))
1249 a8083063 Iustin Pop
    meta = self._children[1]
1250 a8083063 Iustin Pop
    if meta is not None:
1251 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == (meta.major, meta.minor))
1252 a8083063 Iustin Pop
      retval = retval and (info["meta_index"] == 0)
1253 a8083063 Iustin Pop
    else:
1254 a8083063 Iustin Pop
      retval = retval and (info["meta_dev"] == "internal" and
1255 a8083063 Iustin Pop
                           info["meta_index"] == -1)
1256 a8083063 Iustin Pop
    return retval
1257 a8083063 Iustin Pop
1258 a8083063 Iustin Pop
  def _MatchesNet(self, info):
1259 a8083063 Iustin Pop
    """Test if our network config matches with an existing device.
1260 a8083063 Iustin Pop

1261 a8083063 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1262 a8083063 Iustin Pop
    method tests if our network configuration is the same as the one
1263 a8083063 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1264 a8083063 Iustin Pop
    device.
1265 a8083063 Iustin Pop

1266 a8083063 Iustin Pop
    """
1267 a8083063 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1268 a8083063 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1269 a8083063 Iustin Pop
      return True
1270 a8083063 Iustin Pop
1271 a8083063 Iustin Pop
    if self._lhost is None:
1272 a8083063 Iustin Pop
      return False
1273 a8083063 Iustin Pop
1274 a8083063 Iustin Pop
    if not ("local_addr" in info and
1275 a8083063 Iustin Pop
            "remote_addr" in info):
1276 a8083063 Iustin Pop
      return False
1277 a8083063 Iustin Pop
1278 a8083063 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1279 a8083063 Iustin Pop
    retval = (retval and
1280 a8083063 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1281 a8083063 Iustin Pop
    return retval
1282 a8083063 Iustin Pop
1283 a8083063 Iustin Pop
  @classmethod
1284 a8083063 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1285 a8083063 Iustin Pop
    """Configure the local part of a DRBD device.
1286 a8083063 Iustin Pop

1287 a8083063 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1288 a8083063 Iustin Pop
    device. And it must be done only once.
1289 a8083063 Iustin Pop

1290 a8083063 Iustin Pop
    """
1291 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta):
1292 a8083063 Iustin Pop
      return False
1293 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1294 a8083063 Iustin Pop
                           backend, meta, "0", "-e", "detach"])
1295 a8083063 Iustin Pop
    if result.failed:
1296 a8083063 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1297 a8083063 Iustin Pop
    return not result.failed
1298 a8083063 Iustin Pop
1299 a8083063 Iustin Pop
  @classmethod
1300 a8083063 Iustin Pop
  def _ShutdownLocal(cls, minor):
1301 a8083063 Iustin Pop
    """Detach from the local device.
1302 a8083063 Iustin Pop

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

1306 a8083063 Iustin Pop
    """
1307 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1308 a8083063 Iustin Pop
    if result.failed:
1309 a8083063 Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1310 a8083063 Iustin Pop
    return not result.failed
1311 a8083063 Iustin Pop
1312 a8083063 Iustin Pop
  @staticmethod
1313 a8083063 Iustin Pop
  def _ShutdownAll(minor):
1314 a8083063 Iustin Pop
    """Deactivate the device.
1315 a8083063 Iustin Pop

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

1318 a8083063 Iustin Pop
    """
1319 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", DRBDev._DevPath(minor), "down"])
1320 a8083063 Iustin Pop
    if result.failed:
1321 a8083063 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1322 a8083063 Iustin Pop
    return not result.failed
1323 a8083063 Iustin Pop
1324 a8083063 Iustin Pop
  @classmethod
1325 a8083063 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol):
1326 a8083063 Iustin Pop
    """Configure the network part of the device.
1327 a8083063 Iustin Pop

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

1335 a8083063 Iustin Pop
    """
1336 a8083063 Iustin Pop
    lhost, lport, rhost, rport = net_info
1337 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "net",
1338 a8083063 Iustin Pop
                           "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport),
1339 a8083063 Iustin Pop
                           protocol])
1340 a8083063 Iustin Pop
    if result.failed:
1341 a8083063 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1342 a8083063 Iustin Pop
                   result.fail_reason)
1343 a8083063 Iustin Pop
      return False
1344 a8083063 Iustin Pop
1345 a8083063 Iustin Pop
    timeout = time.time() + 10
1346 a8083063 Iustin Pop
    ok = False
1347 a8083063 Iustin Pop
    while time.time() < timeout:
1348 a8083063 Iustin Pop
      info = cls._GetDevInfo(minor)
1349 a8083063 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1350 a8083063 Iustin Pop
        time.sleep(1)
1351 a8083063 Iustin Pop
        continue
1352 a8083063 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1353 a8083063 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1354 a8083063 Iustin Pop
        time.sleep(1)
1355 a8083063 Iustin Pop
        continue
1356 a8083063 Iustin Pop
      ok = True
1357 a8083063 Iustin Pop
      break
1358 a8083063 Iustin Pop
    if not ok:
1359 a8083063 Iustin Pop
      logger.Error("Timeout while configuring network")
1360 a8083063 Iustin Pop
      return False
1361 a8083063 Iustin Pop
    return True
1362 a8083063 Iustin Pop
1363 a8083063 Iustin Pop
  @classmethod
1364 a8083063 Iustin Pop
  def _ShutdownNet(cls, minor):
1365 a8083063 Iustin Pop
    """Disconnect from the remote peer.
1366 a8083063 Iustin Pop

1367 a8083063 Iustin Pop
    This fails if we don't have a local device.
1368 a8083063 Iustin Pop

1369 a8083063 Iustin Pop
    """
1370 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1371 a8459f1c Iustin Pop
    if result.failed:
1372 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1373 a8083063 Iustin Pop
    return not result.failed
1374 a8083063 Iustin Pop
1375 a8083063 Iustin Pop
  def Assemble(self):
1376 a8083063 Iustin Pop
    """Assemble the drbd.
1377 a8083063 Iustin Pop

1378 a8083063 Iustin Pop
    Method:
1379 a8083063 Iustin Pop
      - if we have a local backing device, we bind to it by:
1380 a8083063 Iustin Pop
        - checking the list of used drbd devices
1381 a8083063 Iustin Pop
        - check if the local minor use of any of them is our own device
1382 a8083063 Iustin Pop
        - if yes, abort?
1383 a8083063 Iustin Pop
        - if not, bind
1384 a8083063 Iustin Pop
      - if we have a local/remote net info:
1385 a8083063 Iustin Pop
        - redo the local backing device step for the remote device
1386 a8083063 Iustin Pop
        - check if any drbd device is using the local port,
1387 a8083063 Iustin Pop
          if yes abort
1388 a8083063 Iustin Pop
        - check if any remote drbd device is using the remote
1389 a8083063 Iustin Pop
          port, if yes abort (for now)
1390 a8083063 Iustin Pop
        - bind our net port
1391 a8083063 Iustin Pop
        - bind the remote net port
1392 a8083063 Iustin Pop

1393 a8083063 Iustin Pop
    """
1394 a8083063 Iustin Pop
    self.Attach()
1395 a8083063 Iustin Pop
    if self.minor is not None:
1396 a8083063 Iustin Pop
      logger.Info("Already assembled")
1397 a8083063 Iustin Pop
      return True
1398 a8083063 Iustin Pop
1399 a8083063 Iustin Pop
    result = super(DRBDev, self).Assemble()
1400 a8083063 Iustin Pop
    if not result:
1401 a8083063 Iustin Pop
      return result
1402 a8083063 Iustin Pop
1403 a8083063 Iustin Pop
    minor = self._FindUnusedMinor()
1404 a8083063 Iustin Pop
    need_localdev_teardown = False
1405 a8083063 Iustin Pop
    if self._children[0]:
1406 a8083063 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1407 a8083063 Iustin Pop
                                   self._children[1].dev_path)
1408 a8083063 Iustin Pop
      if not result:
1409 a8083063 Iustin Pop
        return False
1410 a8083063 Iustin Pop
      need_localdev_teardown = True
1411 a8083063 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1412 a8083063 Iustin Pop
      result = self._AssembleNet(minor,
1413 a8083063 Iustin Pop
                                 (self._lhost, self._lport,
1414 a8083063 Iustin Pop
                                  self._rhost, self._rport),
1415 a8083063 Iustin Pop
                                 "C")
1416 a8083063 Iustin Pop
      if not result:
1417 a8083063 Iustin Pop
        if need_localdev_teardown:
1418 a8083063 Iustin Pop
          # we will ignore failures from this
1419 a8083063 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1420 a8083063 Iustin Pop
          self._ShutdownAll(minor)
1421 a8083063 Iustin Pop
        return False
1422 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1423 a8083063 Iustin Pop
    return True
1424 a8083063 Iustin Pop
1425 a8083063 Iustin Pop
  def Shutdown(self):
1426 a8083063 Iustin Pop
    """Shutdown the DRBD device.
1427 a8083063 Iustin Pop

1428 a8083063 Iustin Pop
    """
1429 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1430 a8083063 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1431 a8083063 Iustin Pop
      return True
1432 a8083063 Iustin Pop
    if not self._ShutdownAll(self.minor):
1433 a8083063 Iustin Pop
      return False
1434 a8083063 Iustin Pop
    self.minor = None
1435 a8083063 Iustin Pop
    self.dev_path = None
1436 a8083063 Iustin Pop
    return True
1437 a8083063 Iustin Pop
1438 a8083063 Iustin Pop
  def Attach(self):
1439 a8083063 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1440 a8083063 Iustin Pop

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

1445 a8083063 Iustin Pop
    """
1446 a8083063 Iustin Pop
    for minor in self._GetUsedDevs():
1447 a8083063 Iustin Pop
      info = self._GetDevInfo(minor)
1448 a8083063 Iustin Pop
      match_l = self._MatchesLocal(info)
1449 a8083063 Iustin Pop
      match_r = self._MatchesNet(info)
1450 a8083063 Iustin Pop
      if match_l and match_r:
1451 a8083063 Iustin Pop
        break
1452 a8083063 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1453 a8083063 Iustin Pop
        res_r = self._AssembleNet(minor,
1454 a8083063 Iustin Pop
                                  (self._lhost, self._lport,
1455 a8083063 Iustin Pop
                                   self._rhost, self._rport),
1456 a8083063 Iustin Pop
                                  "C")
1457 a8083063 Iustin Pop
        if res_r and self._MatchesNet(self._GetDevInfo(minor)):
1458 a8083063 Iustin Pop
          break
1459 a8083063 Iustin Pop
    else:
1460 a8083063 Iustin Pop
      minor = None
1461 a8083063 Iustin Pop
1462 a8083063 Iustin Pop
    self._SetFromMinor(minor)
1463 a8083063 Iustin Pop
    return minor is not None
1464 a8083063 Iustin Pop
1465 a8083063 Iustin Pop
  def Open(self, force=False):
1466 a8083063 Iustin Pop
    """Make the local state primary.
1467 a8083063 Iustin Pop

1468 a8083063 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
1469 a8083063 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
1470 a8083063 Iustin Pop
    force flag should be only given after creation, when it actually
1471 a8083063 Iustin Pop
    has to be given.
1472 a8083063 Iustin Pop

1473 a8083063 Iustin Pop
    """
1474 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1475 a8083063 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1476 a8083063 Iustin Pop
      return False
1477 a8083063 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1478 a8083063 Iustin Pop
    if force:
1479 a8083063 Iustin Pop
      cmd.append("--do-what-I-say")
1480 a8083063 Iustin Pop
    result = utils.RunCmd(cmd)
1481 a8083063 Iustin Pop
    if result.failed:
1482 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1483 fdbd668d Iustin Pop
      logger.Error(msg)
1484 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1485 a8083063 Iustin Pop
1486 a8083063 Iustin Pop
  def Close(self):
1487 a8083063 Iustin Pop
    """Make the local state secondary.
1488 a8083063 Iustin Pop

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

1491 a8083063 Iustin Pop
    """
1492 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1493 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1494 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1495 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1496 a8083063 Iustin Pop
    if result.failed:
1497 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1498 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1499 fdbd668d Iustin Pop
      logger.Error(msg)
1500 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1501 a8083063 Iustin Pop
1502 a8083063 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1503 a8083063 Iustin Pop
    """Set the speed of the DRBD syncer.
1504 a8083063 Iustin Pop

1505 a8083063 Iustin Pop
    """
1506 a8083063 Iustin Pop
    children_result = super(DRBDev, self).SetSyncSpeed(kbytes)
1507 a8083063 Iustin Pop
    if self.minor is None:
1508 a8083063 Iustin Pop
      logger.Info("Instance not attached to a device")
1509 a8083063 Iustin Pop
      return False
1510 a8083063 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1511 a8083063 Iustin Pop
                           kbytes])
1512 a8083063 Iustin Pop
    if result.failed:
1513 a8083063 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
1514 a8083063 Iustin Pop
    return not result.failed and children_result
1515 a8083063 Iustin Pop
1516 a8083063 Iustin Pop
  def GetSyncStatus(self):
1517 a8083063 Iustin Pop
    """Returns the sync status of the device.
1518 a8083063 Iustin Pop

1519 a8083063 Iustin Pop
    Returns:
1520 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
1521 a8083063 Iustin Pop

1522 a8083063 Iustin Pop
    If sync_percent is None, it means all is ok
1523 a8083063 Iustin Pop
    If estimated_time is None, it means we can't esimate
1524 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1525 0834c866 Iustin Pop

1526 0834c866 Iustin Pop
    The ldisk parameter will be returned as True, since the DRBD7
1527 0834c866 Iustin Pop
    devices have not been converted.
1528 a8083063 Iustin Pop

1529 a8083063 Iustin Pop
    """
1530 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1531 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1532 a8083063 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1533 a8083063 Iustin Pop
    if self.minor not in proc_info:
1534 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1535 a8083063 Iustin Pop
                                    self.minor)
1536 a8083063 Iustin Pop
    line = proc_info[self.minor]
1537 a8083063 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1538 a8083063 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1539 a8083063 Iustin Pop
    if match:
1540 a8083063 Iustin Pop
      sync_percent = float(match.group(1))
1541 a8083063 Iustin Pop
      hours = int(match.group(2))
1542 a8083063 Iustin Pop
      minutes = int(match.group(3))
1543 a8083063 Iustin Pop
      seconds = int(match.group(4))
1544 a8083063 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1545 a8083063 Iustin Pop
    else:
1546 a8083063 Iustin Pop
      sync_percent = None
1547 a8083063 Iustin Pop
      est_time = None
1548 a8083063 Iustin Pop
    match = re.match("^ *[0-9]+: cs:([^ ]+).*$", line)
1549 a8083063 Iustin Pop
    if not match:
1550 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1551 a8083063 Iustin Pop
                                    self.minor)
1552 a8083063 Iustin Pop
    client_state = match.group(1)
1553 a8083063 Iustin Pop
    is_degraded = client_state != "Connected"
1554 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded, False
1555 a8083063 Iustin Pop
1556 a8083063 Iustin Pop
  def GetStatus(self):
1557 a8083063 Iustin Pop
    """Compute the status of the DRBD device
1558 a8083063 Iustin Pop

1559 a8083063 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
1560 a8083063 Iustin Pop

1561 a8083063 Iustin Pop
    """
1562 a8083063 Iustin Pop
    if self.minor is None and not self.Attach():
1563 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1564 a8083063 Iustin Pop
1565 a8083063 Iustin Pop
    data = self._GetProcData()
1566 a8083063 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
1567 a8083063 Iustin Pop
                       self.minor)
1568 a8083063 Iustin Pop
    for line in data:
1569 a8083063 Iustin Pop
      mresult = match.match(line)
1570 a8083063 Iustin Pop
      if mresult:
1571 a8083063 Iustin Pop
        break
1572 a8083063 Iustin Pop
    else:
1573 a8083063 Iustin Pop
      logger.Error("Can't find myself!")
1574 a8083063 Iustin Pop
      return self.STATUS_UNKNOWN
1575 a8083063 Iustin Pop
1576 a8083063 Iustin Pop
    state = mresult.group(2)
1577 a8083063 Iustin Pop
    if state == "Primary":
1578 a8083063 Iustin Pop
      result = self.STATUS_ONLINE
1579 a8083063 Iustin Pop
    else:
1580 a8083063 Iustin Pop
      result = self.STATUS_STANDBY
1581 a8083063 Iustin Pop
1582 a8083063 Iustin Pop
    return result
1583 a8083063 Iustin Pop
1584 a8083063 Iustin Pop
  @staticmethod
1585 a8083063 Iustin Pop
  def _ZeroDevice(device):
1586 a8083063 Iustin Pop
    """Zero a device.
1587 a8083063 Iustin Pop

1588 a8083063 Iustin Pop
    This writes until we get ENOSPC.
1589 a8083063 Iustin Pop

1590 a8083063 Iustin Pop
    """
1591 a8083063 Iustin Pop
    f = open(device, "w")
1592 a8083063 Iustin Pop
    buf = "\0" * 1048576
1593 a8083063 Iustin Pop
    try:
1594 a8083063 Iustin Pop
      while True:
1595 a8083063 Iustin Pop
        f.write(buf)
1596 a8083063 Iustin Pop
    except IOError, err:
1597 a8083063 Iustin Pop
      if err.errno != errno.ENOSPC:
1598 a8083063 Iustin Pop
        raise
1599 a8083063 Iustin Pop
1600 a8083063 Iustin Pop
  @classmethod
1601 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
1602 a8083063 Iustin Pop
    """Create a new DRBD device.
1603 a8083063 Iustin Pop

1604 a8083063 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1605 a8083063 Iustin Pop
    function just zeroes the meta device.
1606 a8083063 Iustin Pop

1607 a8083063 Iustin Pop
    """
1608 a8083063 Iustin Pop
    if len(children) != 2:
1609 a8083063 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1610 a8083063 Iustin Pop
    meta = children[1]
1611 a8083063 Iustin Pop
    meta.Assemble()
1612 a8083063 Iustin Pop
    if not meta.Attach():
1613 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1614 ae26a287 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1615 a8083063 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device")
1616 a8083063 Iustin Pop
    logger.Info("Started zeroing device %s" % meta.dev_path)
1617 a8083063 Iustin Pop
    cls._ZeroDevice(meta.dev_path)
1618 a8083063 Iustin Pop
    logger.Info("Done zeroing device %s" % meta.dev_path)
1619 a8083063 Iustin Pop
    return cls(unique_id, children)
1620 a8083063 Iustin Pop
1621 a8083063 Iustin Pop
  def Remove(self):
1622 a8083063 Iustin Pop
    """Stub remove for DRBD devices.
1623 a8083063 Iustin Pop

1624 a8083063 Iustin Pop
    """
1625 a8083063 Iustin Pop
    return self.Shutdown()
1626 a8083063 Iustin Pop
1627 f3e513ad Iustin Pop
1628 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
1629 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
1630 a2cfdea2 Iustin Pop

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

1635 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
1636 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
1637 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
1638 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
1639 a2cfdea2 Iustin Pop

1640 a2cfdea2 Iustin Pop
  """
1641 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
1642 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
1643 a2cfdea2 Iustin Pop
1644 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
1645 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
1646 fc1dc9d7 Iustin Pop
      children = []
1647 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
1648 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
1649 c3f9340c Guido Trotter
    version = self._GetVersion()
1650 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
1651 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
1652 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
1653 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
1654 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
1655 a2cfdea2 Iustin Pop
1656 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
1657 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
1658 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
1659 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1660 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
1661 a2cfdea2 Iustin Pop
    self.Attach()
1662 a2cfdea2 Iustin Pop
1663 a2cfdea2 Iustin Pop
  @classmethod
1664 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
1665 a2cfdea2 Iustin Pop
    """Initialize a meta device.
1666 a2cfdea2 Iustin Pop

1667 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
1668 a2cfdea2 Iustin Pop

1669 a2cfdea2 Iustin Pop
    """
1670 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
1671 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
1672 a2cfdea2 Iustin Pop
    if result.failed:
1673 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
1674 a2cfdea2 Iustin Pop
                                    result.output)
1675 a2cfdea2 Iustin Pop
1676 a2cfdea2 Iustin Pop
  @classmethod
1677 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
1678 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
1679 a2cfdea2 Iustin Pop

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

1683 a2cfdea2 Iustin Pop
    """
1684 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
1685 a2cfdea2 Iustin Pop
1686 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
1687 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
1688 a2cfdea2 Iustin Pop
    highest = None
1689 a2cfdea2 Iustin Pop
    for line in data:
1690 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
1691 a2cfdea2 Iustin Pop
      if match:
1692 a2cfdea2 Iustin Pop
        return int(match.group(1))
1693 a2cfdea2 Iustin Pop
      match = used_line.match(line)
1694 a2cfdea2 Iustin Pop
      if match:
1695 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
1696 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
1697 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
1698 a2cfdea2 Iustin Pop
      return 0
1699 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
1700 a2cfdea2 Iustin Pop
      logger.Error("Error: no free drbd minors!")
1701 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
1702 a2cfdea2 Iustin Pop
    return highest + 1
1703 a2cfdea2 Iustin Pop
1704 a2cfdea2 Iustin Pop
  @classmethod
1705 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
1706 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
1707 a2cfdea2 Iustin Pop

1708 a2cfdea2 Iustin Pop
    """
1709 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
1710 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
1711 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
1712 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
1713 a2cfdea2 Iustin Pop
                           "dstate"])
1714 a2cfdea2 Iustin Pop
    if result.failed:
1715 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
1716 a2cfdea2 Iustin Pop
      return False
1717 a2cfdea2 Iustin Pop
    return True
1718 a2cfdea2 Iustin Pop
1719 a2cfdea2 Iustin Pop
  @classmethod
1720 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
1721 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
1722 a2cfdea2 Iustin Pop

1723 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
1724 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
1725 a2cfdea2 Iustin Pop

1726 a2cfdea2 Iustin Pop
    """
1727 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
1728 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
1729 a2cfdea2 Iustin Pop
1730 a2cfdea2 Iustin Pop
    # pyparsing setup
1731 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
1732 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
1733 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
1734 a2cfdea2 Iustin Pop
    # this also converts the value to an int
1735 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
1736 a2cfdea2 Iustin Pop
1737 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
1738 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
1739 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
1740 a2cfdea2 Iustin Pop
1741 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
1742 a2cfdea2 Iustin Pop
1743 a2cfdea2 Iustin Pop
    # value types
1744 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
1745 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
1746 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
1747 a2cfdea2 Iustin Pop
                 number)
1748 a2cfdea2 Iustin Pop
    # meta device, extended syntax
1749 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
1750 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
1751 a2cfdea2 Iustin Pop
1752 a2cfdea2 Iustin Pop
    # a statement
1753 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
1754 a2cfdea2 Iustin Pop
            (addr_port ^ value ^ quoted ^ meta_value) +
1755 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
1756 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
1757 a2cfdea2 Iustin Pop
1758 a2cfdea2 Iustin Pop
    # an entire section
1759 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
1760 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
1761 a2cfdea2 Iustin Pop
1762 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
1763 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
1764 a2cfdea2 Iustin Pop
1765 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
1766 a2cfdea2 Iustin Pop
1767 a2cfdea2 Iustin Pop
    return bnf
1768 a2cfdea2 Iustin Pop
1769 a2cfdea2 Iustin Pop
  @classmethod
1770 3840729d Iustin Pop
  def _GetShowData(cls, minor):
1771 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
1772 a2cfdea2 Iustin Pop

1773 a2cfdea2 Iustin Pop
    """
1774 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
1775 a2cfdea2 Iustin Pop
    if result.failed:
1776 a2cfdea2 Iustin Pop
      logger.Error("Can't display the drbd config: %s" % result.fail_reason)
1777 3840729d Iustin Pop
      return None
1778 3840729d Iustin Pop
    return result.stdout
1779 3840729d Iustin Pop
1780 3840729d Iustin Pop
  @classmethod
1781 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
1782 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
1783 3840729d Iustin Pop

1784 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
1785 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
1786 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
1787 3840729d Iustin Pop
    by _GetShowData.
1788 3840729d Iustin Pop

1789 3840729d Iustin Pop
    """
1790 3840729d Iustin Pop
    data = {}
1791 a2cfdea2 Iustin Pop
    if not out:
1792 a2cfdea2 Iustin Pop
      return data
1793 a2cfdea2 Iustin Pop
1794 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
1795 a2cfdea2 Iustin Pop
    # run pyparse
1796 a2cfdea2 Iustin Pop
1797 a2cfdea2 Iustin Pop
    try:
1798 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
1799 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
1800 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
1801 a2cfdea2 Iustin Pop
                                    str(err))
1802 a2cfdea2 Iustin Pop
1803 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
1804 a2cfdea2 Iustin Pop
    for section in results:
1805 a2cfdea2 Iustin Pop
      sname = section[0]
1806 a2cfdea2 Iustin Pop
      if sname == "_this_host":
1807 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1808 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
1809 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
1810 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
1811 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
1812 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
1813 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
1814 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
1815 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
1816 a2cfdea2 Iustin Pop
        for lst in section[1:]:
1817 a2cfdea2 Iustin Pop
          if lst[0] == "address":
1818 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
1819 a2cfdea2 Iustin Pop
    return data
1820 a2cfdea2 Iustin Pop
1821 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
1822 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
1823 a2cfdea2 Iustin Pop

1824 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1825 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
1826 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
1827 a2cfdea2 Iustin Pop
    device.
1828 a2cfdea2 Iustin Pop

1829 a2cfdea2 Iustin Pop
    """
1830 b00b95dd Iustin Pop
    if self._children:
1831 b00b95dd Iustin Pop
      backend, meta = self._children
1832 b00b95dd Iustin Pop
    else:
1833 b00b95dd Iustin Pop
      backend = meta = None
1834 b00b95dd Iustin Pop
1835 a2cfdea2 Iustin Pop
    if backend is not None:
1836 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
1837 a2cfdea2 Iustin Pop
    else:
1838 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
1839 b00b95dd Iustin Pop
1840 a2cfdea2 Iustin Pop
    if meta is not None:
1841 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
1842 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
1843 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
1844 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1845 a2cfdea2 Iustin Pop
    else:
1846 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1847 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1848 a2cfdea2 Iustin Pop
    return retval
1849 a2cfdea2 Iustin Pop
1850 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1851 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1852 a2cfdea2 Iustin Pop

1853 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1854 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1855 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1856 a2cfdea2 Iustin Pop
    device.
1857 a2cfdea2 Iustin Pop

1858 a2cfdea2 Iustin Pop
    """
1859 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1860 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1861 a2cfdea2 Iustin Pop
      return True
1862 a2cfdea2 Iustin Pop
1863 a2cfdea2 Iustin Pop
    if self._lhost is None:
1864 a2cfdea2 Iustin Pop
      return False
1865 a2cfdea2 Iustin Pop
1866 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1867 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1868 a2cfdea2 Iustin Pop
      return False
1869 a2cfdea2 Iustin Pop
1870 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1871 a2cfdea2 Iustin Pop
    retval = (retval and
1872 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1873 a2cfdea2 Iustin Pop
    return retval
1874 a2cfdea2 Iustin Pop
1875 a2cfdea2 Iustin Pop
  @classmethod
1876 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1877 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1878 a2cfdea2 Iustin Pop

1879 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1880 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1881 a2cfdea2 Iustin Pop

1882 a2cfdea2 Iustin Pop
    """
1883 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
1884 a2cfdea2 Iustin Pop
      return False
1885 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disk",
1886 a2cfdea2 Iustin Pop
                           backend, meta, "0", "-e", "detach",
1887 a2cfdea2 Iustin Pop
                           "--create-device"])
1888 a2cfdea2 Iustin Pop
    if result.failed:
1889 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
1890 a2cfdea2 Iustin Pop
    return not result.failed
1891 a2cfdea2 Iustin Pop
1892 a2cfdea2 Iustin Pop
  @classmethod
1893 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1894 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1895 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1896 a2cfdea2 Iustin Pop

1897 a2cfdea2 Iustin Pop
    """
1898 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
1899 52857176 Iustin Pop
    if None in net_info:
1900 52857176 Iustin Pop
      # we don't want network connection and actually want to make
1901 52857176 Iustin Pop
      # sure its shutdown
1902 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
1903 52857176 Iustin Pop
1904 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
1905 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
1906 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
1907 f38478b2 Iustin Pop
            "-B", "consensus",
1908 f38478b2 Iustin Pop
            ]
1909 a2cfdea2 Iustin Pop
    if dual_pri:
1910 a2cfdea2 Iustin Pop
      args.append("-m")
1911 a2cfdea2 Iustin Pop
    if hmac and secret:
1912 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
1913 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
1914 a2cfdea2 Iustin Pop
    if result.failed:
1915 a2cfdea2 Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s" %
1916 a2cfdea2 Iustin Pop
                   result.fail_reason)
1917 a2cfdea2 Iustin Pop
      return False
1918 a2cfdea2 Iustin Pop
1919 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
1920 a2cfdea2 Iustin Pop
    ok = False
1921 a2cfdea2 Iustin Pop
    while time.time() < timeout:
1922 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
1923 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
1924 a2cfdea2 Iustin Pop
        time.sleep(1)
1925 a2cfdea2 Iustin Pop
        continue
1926 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
1927 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
1928 a2cfdea2 Iustin Pop
        time.sleep(1)
1929 a2cfdea2 Iustin Pop
        continue
1930 a2cfdea2 Iustin Pop
      ok = True
1931 a2cfdea2 Iustin Pop
      break
1932 a2cfdea2 Iustin Pop
    if not ok:
1933 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
1934 a2cfdea2 Iustin Pop
      return False
1935 a2cfdea2 Iustin Pop
    return True
1936 a2cfdea2 Iustin Pop
1937 b00b95dd Iustin Pop
  def AddChildren(self, devices):
1938 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
1939 b00b95dd Iustin Pop

1940 b00b95dd Iustin Pop
    """
1941 b00b95dd Iustin Pop
    if self.minor is None:
1942 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1943 b00b95dd Iustin Pop
    if len(devices) != 2:
1944 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1945 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1946 03ece5f3 Iustin Pop
    if "local_dev" in info:
1947 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1948 b00b95dd Iustin Pop
    backend, meta = devices
1949 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1950 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1951 b00b95dd Iustin Pop
    backend.Open()
1952 b00b95dd Iustin Pop
    meta.Open()
1953 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1954 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1955 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1956 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1957 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1958 b00b95dd Iustin Pop
1959 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1960 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1961 b00b95dd Iustin Pop
    self._children = devices
1962 b00b95dd Iustin Pop
1963 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1964 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1965 b00b95dd Iustin Pop

1966 b00b95dd Iustin Pop
    """
1967 b00b95dd Iustin Pop
    if self.minor is None:
1968 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1969 b00b95dd Iustin Pop
                                    " RemoveChildren")
1970 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1971 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1972 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1973 03ece5f3 Iustin Pop
      return
1974 b00b95dd Iustin Pop
    if len(self._children) != 2:
1975 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1976 b00b95dd Iustin Pop
                                    self._children)
1977 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1978 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1979 e739bd57 Iustin Pop
      return
1980 b00b95dd Iustin Pop
    if len(devices) != 2:
1981 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1982 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1983 e739bd57 Iustin Pop
      if dev != child.dev_path:
1984 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1985 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1986 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1987 b00b95dd Iustin Pop
1988 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1989 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1990 b00b95dd Iustin Pop
    self._children = []
1991 b00b95dd Iustin Pop
1992 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1993 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1994 a2cfdea2 Iustin Pop

1995 a2cfdea2 Iustin Pop
    """
1996 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1997 a2cfdea2 Iustin Pop
    if self.minor is None:
1998 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1999 a2cfdea2 Iustin Pop
      return False
2000 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
2001 a2cfdea2 Iustin Pop
                           kbytes])
2002 a2cfdea2 Iustin Pop
    if result.failed:
2003 a2cfdea2 Iustin Pop
      logger.Error("Can't change syncer rate: %s " % result.fail_reason)
2004 a2cfdea2 Iustin Pop
    return not result.failed and children_result
2005 a2cfdea2 Iustin Pop
2006 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
2007 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
2008 a2cfdea2 Iustin Pop

2009 a2cfdea2 Iustin Pop
    Returns:
2010 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
2011 a2cfdea2 Iustin Pop

2012 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
2013 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
2014 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
2015 0834c866 Iustin Pop

2016 0834c866 Iustin Pop

2017 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
2018 0834c866 Iustin Pop
    network not connected or local disk missing.
2019 0834c866 Iustin Pop

2020 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
2021 0834c866 Iustin Pop
    disk or not.
2022 a2cfdea2 Iustin Pop

2023 a2cfdea2 Iustin Pop
    """
2024 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2025 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
2026 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
2027 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
2028 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
2029 a2cfdea2 Iustin Pop
                                    self.minor)
2030 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
2031 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
2032 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
2033 a2cfdea2 Iustin Pop
    if match:
2034 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
2035 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
2036 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
2037 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
2038 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
2039 a2cfdea2 Iustin Pop
    else:
2040 a2cfdea2 Iustin Pop
      sync_percent = None
2041 a2cfdea2 Iustin Pop
      est_time = None
2042 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
2043 a2cfdea2 Iustin Pop
    if not match:
2044 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
2045 a2cfdea2 Iustin Pop
                                    self.minor)
2046 a2cfdea2 Iustin Pop
    client_state = match.group(1)
2047 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
2048 0834c866 Iustin Pop
    ldisk = local_disk_state != "UpToDate"
2049 0834c866 Iustin Pop
    is_degraded = client_state != "Connected"
2050 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded or ldisk, ldisk
2051 a2cfdea2 Iustin Pop
2052 a2cfdea2 Iustin Pop
  def GetStatus(self):
2053 a2cfdea2 Iustin Pop
    """Compute the status of the DRBD device
2054 a2cfdea2 Iustin Pop

2055 a2cfdea2 Iustin Pop
    Note that DRBD devices don't have the STATUS_EXISTING state.
2056 a2cfdea2 Iustin Pop

2057 a2cfdea2 Iustin Pop
    """
2058 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2059 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2060 a2cfdea2 Iustin Pop
2061 a2cfdea2 Iustin Pop
    data = self._GetProcData()
2062 a2cfdea2 Iustin Pop
    match = re.compile("^ *%d: cs:[^ ]+ st:(Primary|Secondary)/.*$" %
2063 a2cfdea2 Iustin Pop
                       self.minor)
2064 a2cfdea2 Iustin Pop
    for line in data:
2065 a2cfdea2 Iustin Pop
      mresult = match.match(line)
2066 a2cfdea2 Iustin Pop
      if mresult:
2067 a2cfdea2 Iustin Pop
        break
2068 a2cfdea2 Iustin Pop
    else:
2069 a2cfdea2 Iustin Pop
      logger.Error("Can't find myself!")
2070 a2cfdea2 Iustin Pop
      return self.STATUS_UNKNOWN
2071 a2cfdea2 Iustin Pop
2072 a2cfdea2 Iustin Pop
    state = mresult.group(2)
2073 a2cfdea2 Iustin Pop
    if state == "Primary":
2074 a2cfdea2 Iustin Pop
      result = self.STATUS_ONLINE
2075 a2cfdea2 Iustin Pop
    else:
2076 a2cfdea2 Iustin Pop
      result = self.STATUS_STANDBY
2077 a2cfdea2 Iustin Pop
2078 a2cfdea2 Iustin Pop
    return result
2079 a2cfdea2 Iustin Pop
2080 a2cfdea2 Iustin Pop
  def Open(self, force=False):
2081 a2cfdea2 Iustin Pop
    """Make the local state primary.
2082 a2cfdea2 Iustin Pop

2083 a2cfdea2 Iustin Pop
    If the 'force' parameter is given, the '--do-what-I-say' parameter
2084 a2cfdea2 Iustin Pop
    is given. Since this is a pottentialy dangerous operation, the
2085 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
2086 a2cfdea2 Iustin Pop
    has to be given.
2087 a2cfdea2 Iustin Pop

2088 a2cfdea2 Iustin Pop
    """
2089 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2090 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
2091 a2cfdea2 Iustin Pop
      return False
2092 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
2093 a2cfdea2 Iustin Pop
    if force:
2094 a2cfdea2 Iustin Pop
      cmd.append("-o")
2095 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
2096 a2cfdea2 Iustin Pop
    if result.failed:
2097 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
2098 fdbd668d Iustin Pop
      logger.Error(msg)
2099 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
2100 a2cfdea2 Iustin Pop
2101 a2cfdea2 Iustin Pop
  def Close(self):
2102 a2cfdea2 Iustin Pop
    """Make the local state secondary.
2103 a2cfdea2 Iustin Pop

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

2106 a2cfdea2 Iustin Pop
    """
2107 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2108 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
2109 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
2110 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
2111 a2cfdea2 Iustin Pop
    if result.failed:
2112 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
2113 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
2114 fdbd668d Iustin Pop
      logger.Error(msg)
2115 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
2116 a2cfdea2 Iustin Pop
2117 a2cfdea2 Iustin Pop
  def Attach(self):
2118 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
2119 a2cfdea2 Iustin Pop

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

2124 a2cfdea2 Iustin Pop
    """
2125 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
2126 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
2127 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
2128 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
2129 a2cfdea2 Iustin Pop
      if match_l and match_r:
2130 a2cfdea2 Iustin Pop
        break
2131 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
2132 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
2133 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
2134 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
2135 a2cfdea2 Iustin Pop
                                  "C")
2136 3840729d Iustin Pop
        if res_r:
2137 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
2138 3840729d Iustin Pop
            break
2139 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
2140 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
2141 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
2142 fc1dc9d7 Iustin Pop
        break
2143 9cdbe77f Iustin Pop
      if match_l and not match_r and "local_addr" in info:
2144 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
2145 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
2146 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
2147 9cdbe77f Iustin Pop
        # reconnect to our correct one
2148 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
2149 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
2150 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
2151 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
2152 9cdbe77f Iustin Pop
                                        " the correct peer")
2153 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
2154 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
2155 9cdbe77f Iustin Pop
        # None)
2156 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
2157 9cdbe77f Iustin Pop
                                      self._rhost, self._rport), "C") and
2158 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
2159 9cdbe77f Iustin Pop
          break
2160 9cdbe77f Iustin Pop
2161 a2cfdea2 Iustin Pop
    else:
2162 a2cfdea2 Iustin Pop
      minor = None
2163 a2cfdea2 Iustin Pop
2164 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2165 a2cfdea2 Iustin Pop
    return minor is not None
2166 a2cfdea2 Iustin Pop
2167 a2cfdea2 Iustin Pop
  def Assemble(self):
2168 a2cfdea2 Iustin Pop
    """Assemble the drbd.
2169 a2cfdea2 Iustin Pop

2170 a2cfdea2 Iustin Pop
    Method:
2171 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
2172 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
2173 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
2174 a2cfdea2 Iustin Pop
        - if yes, abort?
2175 a2cfdea2 Iustin Pop
        - if not, bind
2176 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
2177 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
2178 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
2179 a2cfdea2 Iustin Pop
          if yes abort
2180 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
2181 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
2182 a2cfdea2 Iustin Pop
        - bind our net port
2183 a2cfdea2 Iustin Pop
        - bind the remote net port
2184 a2cfdea2 Iustin Pop

2185 a2cfdea2 Iustin Pop
    """
2186 a2cfdea2 Iustin Pop
    self.Attach()
2187 a2cfdea2 Iustin Pop
    if self.minor is not None:
2188 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
2189 a2cfdea2 Iustin Pop
      return True
2190 a2cfdea2 Iustin Pop
2191 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
2192 a2cfdea2 Iustin Pop
    if not result:
2193 a2cfdea2 Iustin Pop
      return result
2194 a2cfdea2 Iustin Pop
2195 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
2196 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
2197 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
2198 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
2199 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
2200 a2cfdea2 Iustin Pop
      if not result:
2201 a2cfdea2 Iustin Pop
        return False
2202 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
2203 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
2204 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
2205 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
2206 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
2207 a2cfdea2 Iustin Pop
                                 "C")
2208 a2cfdea2 Iustin Pop
      if not result:
2209 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
2210 a2cfdea2 Iustin Pop
          # we will ignore failures from this
2211 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
2212 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
2213 a2cfdea2 Iustin Pop
        return False
2214 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
2215 a2cfdea2 Iustin Pop
    return True
2216 a2cfdea2 Iustin Pop
2217 a2cfdea2 Iustin Pop
  @classmethod
2218 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
2219 b00b95dd Iustin Pop
    """Detach from the local device.
2220 b00b95dd Iustin Pop

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

2224 b00b95dd Iustin Pop
    """
2225 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
2226 b00b95dd Iustin Pop
    if result.failed:
2227 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
2228 b00b95dd Iustin Pop
    return not result.failed
2229 b00b95dd Iustin Pop
2230 b00b95dd Iustin Pop
  @classmethod
2231 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
2232 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
2233 f3e513ad Iustin Pop

2234 f3e513ad Iustin Pop
    This fails if we don't have a local device.
2235 f3e513ad Iustin Pop

2236 f3e513ad Iustin Pop
    """
2237 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
2238 a8459f1c Iustin Pop
    if result.failed:
2239 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
2240 f3e513ad Iustin Pop
    return not result.failed
2241 f3e513ad Iustin Pop
2242 f3e513ad Iustin Pop
  @classmethod
2243 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
2244 a2cfdea2 Iustin Pop
    """Deactivate the device.
2245 a2cfdea2 Iustin Pop

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

2248 a2cfdea2 Iustin Pop
    """
2249 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
2250 a2cfdea2 Iustin Pop
    if result.failed:
2251 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
2252 a2cfdea2 Iustin Pop
    return not result.failed
2253 a2cfdea2 Iustin Pop
2254 a2cfdea2 Iustin Pop
  def Shutdown(self):
2255 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
2256 a2cfdea2 Iustin Pop

2257 a2cfdea2 Iustin Pop
    """
2258 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
2259 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
2260 a2cfdea2 Iustin Pop
      return True
2261 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
2262 a2cfdea2 Iustin Pop
      return False
2263 a2cfdea2 Iustin Pop
    self.minor = None
2264 a2cfdea2 Iustin Pop
    self.dev_path = None
2265 a2cfdea2 Iustin Pop
    return True
2266 a2cfdea2 Iustin Pop
2267 a2cfdea2 Iustin Pop
  def Remove(self):
2268 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
2269 a2cfdea2 Iustin Pop

2270 a2cfdea2 Iustin Pop
    """
2271 a2cfdea2 Iustin Pop
    return self.Shutdown()
2272 a2cfdea2 Iustin Pop
2273 a2cfdea2 Iustin Pop
  @classmethod
2274 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
2275 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
2276 a2cfdea2 Iustin Pop

2277 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
2278 a2cfdea2 Iustin Pop
    function only initializes the metadata.
2279 a2cfdea2 Iustin Pop

2280 a2cfdea2 Iustin Pop
    """
2281 a2cfdea2 Iustin Pop
    if len(children) != 2:
2282 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
2283 a2cfdea2 Iustin Pop
    meta = children[1]
2284 a2cfdea2 Iustin Pop
    meta.Assemble()
2285 a2cfdea2 Iustin Pop
    if not meta.Attach():
2286 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
2287 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
2288 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
2289 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
2290 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
2291 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
2292 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
2293 a2cfdea2 Iustin Pop
2294 a8083063 Iustin Pop
2295 a8083063 Iustin Pop
DEV_MAP = {
2296 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
2297 fe96220b Iustin Pop
  constants.LD_MD_R1: MDRaid1,
2298 fe96220b Iustin Pop
  constants.LD_DRBD7: DRBDev,
2299 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
2300 a8083063 Iustin Pop
  }
2301 a8083063 Iustin Pop
2302 a8083063 Iustin Pop
2303 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
2304 a8083063 Iustin Pop
  """Search for an existing, assembled device.
2305 a8083063 Iustin Pop

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

2309 a8083063 Iustin Pop
  """
2310 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2311 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2312 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2313 a8083063 Iustin Pop
  if not device.Attach():
2314 a8083063 Iustin Pop
    return None
2315 a8083063 Iustin Pop
  return  device
2316 a8083063 Iustin Pop
2317 a8083063 Iustin Pop
2318 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
2319 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
2320 a8083063 Iustin Pop

2321 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
2322 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
2323 a8083063 Iustin Pop

2324 a8083063 Iustin Pop
  """
2325 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2326 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2327 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
2328 a8083063 Iustin Pop
  if not device.Attach():
2329 a8083063 Iustin Pop
    device.Assemble()
2330 be1ba2bd Iustin Pop
    if not device.Attach():
2331 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
2332 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
2333 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
2334 a8083063 Iustin Pop
  return device
2335 a8083063 Iustin Pop
2336 a8083063 Iustin Pop
2337 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
2338 a8083063 Iustin Pop
  """Create a device.
2339 a8083063 Iustin Pop

2340 a8083063 Iustin Pop
  """
2341 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
2342 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
2343 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
2344 a8083063 Iustin Pop
  return device