Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ afee0879

History | View | Annotate | Download (54.2 kB)

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

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

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

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

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

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

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

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

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

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

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

98 a8083063 Iustin Pop
    """
99 a8083063 Iustin Pop
    status = True
100 a8083063 Iustin Pop
    for child in self._children:
101 a8083063 Iustin Pop
      if not isinstance(child, BlockDev):
102 a8083063 Iustin Pop
        raise TypeError("Invalid child passed of type '%s'" % type(child))
103 a8083063 Iustin Pop
      if not status:
104 a8083063 Iustin Pop
        break
105 a8083063 Iustin Pop
      status = status and child.Assemble()
106 a8083063 Iustin Pop
      if not status:
107 a8083063 Iustin Pop
        break
108 fdbd668d Iustin Pop
109 fdbd668d Iustin Pop
      try:
110 fdbd668d Iustin Pop
        child.Open()
111 fdbd668d Iustin Pop
      except errors.BlockDeviceError:
112 fdbd668d Iustin Pop
        for child in self._children:
113 fdbd668d Iustin Pop
          child.Shutdown()
114 fdbd668d Iustin Pop
        raise
115 a8083063 Iustin Pop
116 a8083063 Iustin Pop
    if not status:
117 a8083063 Iustin Pop
      for child in self._children:
118 a8083063 Iustin Pop
        child.Shutdown()
119 a8083063 Iustin Pop
    return status
120 a8083063 Iustin Pop
121 a8083063 Iustin Pop
  def Attach(self):
122 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
123 a8083063 Iustin Pop

124 a8083063 Iustin Pop
    """
125 a8083063 Iustin Pop
    raise NotImplementedError
126 a8083063 Iustin Pop
127 a8083063 Iustin Pop
  def Close(self):
128 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
129 a8083063 Iustin Pop

130 a8083063 Iustin Pop
    """
131 a8083063 Iustin Pop
    raise NotImplementedError
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
  @classmethod
134 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
135 a8083063 Iustin Pop
    """Create the device.
136 a8083063 Iustin Pop

137 a8083063 Iustin Pop
    If the device cannot be created, it will return None
138 a8083063 Iustin Pop
    instead. Error messages go to the logging system.
139 a8083063 Iustin Pop

140 a8083063 Iustin Pop
    Note that for some devices, the unique_id is used, and for other,
141 a8083063 Iustin Pop
    the children. The idea is that these two, taken together, are
142 a8083063 Iustin Pop
    enough for both creation and assembly (later).
143 a8083063 Iustin Pop

144 a8083063 Iustin Pop
    """
145 a8083063 Iustin Pop
    raise NotImplementedError
146 a8083063 Iustin Pop
147 a8083063 Iustin Pop
  def Remove(self):
148 a8083063 Iustin Pop
    """Remove this device.
149 a8083063 Iustin Pop

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

154 a8083063 Iustin Pop
    """
155 a8083063 Iustin Pop
    raise NotImplementedError
156 a8083063 Iustin Pop
157 f3e513ad Iustin Pop
  def Rename(self, new_id):
158 f3e513ad Iustin Pop
    """Rename this device.
159 f3e513ad Iustin Pop

160 f3e513ad Iustin Pop
    This may or may not make sense for a given device type.
161 f3e513ad Iustin Pop

162 f3e513ad Iustin Pop
    """
163 f3e513ad Iustin Pop
    raise NotImplementedError
164 f3e513ad Iustin Pop
165 a8083063 Iustin Pop
  def Open(self, force=False):
166 a8083063 Iustin Pop
    """Make the device ready for use.
167 a8083063 Iustin Pop

168 a8083063 Iustin Pop
    This makes the device ready for I/O. For now, just the DRBD
169 a8083063 Iustin Pop
    devices need this.
170 a8083063 Iustin Pop

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

174 a8083063 Iustin Pop
    """
175 a8083063 Iustin Pop
    raise NotImplementedError
176 a8083063 Iustin Pop
177 a8083063 Iustin Pop
  def Shutdown(self):
178 a8083063 Iustin Pop
    """Shut down the device, freeing its children.
179 a8083063 Iustin Pop

180 a8083063 Iustin Pop
    This undoes the `Assemble()` work, except for the child
181 a8083063 Iustin Pop
    assembling; as such, the children on the device are still
182 a8083063 Iustin Pop
    assembled after this call.
183 a8083063 Iustin Pop

184 a8083063 Iustin Pop
    """
185 a8083063 Iustin Pop
    raise NotImplementedError
186 a8083063 Iustin Pop
187 a8083063 Iustin Pop
  def SetSyncSpeed(self, speed):
188 a8083063 Iustin Pop
    """Adjust the sync speed of the mirror.
189 a8083063 Iustin Pop

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

192 a8083063 Iustin Pop
    """
193 a8083063 Iustin Pop
    result = True
194 a8083063 Iustin Pop
    if self._children:
195 a8083063 Iustin Pop
      for child in self._children:
196 a8083063 Iustin Pop
        result = result and child.SetSyncSpeed(speed)
197 a8083063 Iustin Pop
    return result
198 a8083063 Iustin Pop
199 a8083063 Iustin Pop
  def GetSyncStatus(self):
200 a8083063 Iustin Pop
    """Returns the sync status of the device.
201 a8083063 Iustin Pop

202 a8083063 Iustin Pop
    If this device is a mirroring device, this function returns the
203 a8083063 Iustin Pop
    status of the mirror.
204 a8083063 Iustin Pop

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

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

210 a8083063 Iustin Pop
    If is_degraded is True, it means the device is missing
211 a8083063 Iustin Pop
    redundancy. This is usually a sign that something went wrong in
212 a8083063 Iustin Pop
    the device setup, if sync_percent is None.
213 a8083063 Iustin Pop

214 0834c866 Iustin Pop
    The ldisk parameter represents the degradation of the local
215 0834c866 Iustin Pop
    data. This is only valid for some devices, the rest will always
216 0834c866 Iustin Pop
    return False (not degraded).
217 0834c866 Iustin Pop

218 c41eea6e Iustin Pop
    @rtype: tuple
219 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
220 c41eea6e Iustin Pop

221 a8083063 Iustin Pop
    """
222 0834c866 Iustin Pop
    return None, None, False, False
223 a8083063 Iustin Pop
224 a8083063 Iustin Pop
225 a8083063 Iustin Pop
  def CombinedSyncStatus(self):
226 a8083063 Iustin Pop
    """Calculate the mirror status recursively for our children.
227 a8083063 Iustin Pop

228 a8083063 Iustin Pop
    The return value is the same as for `GetSyncStatus()` except the
229 a8083063 Iustin Pop
    minimum percent and maximum time are calculated across our
230 a8083063 Iustin Pop
    children.
231 a8083063 Iustin Pop

232 a8083063 Iustin Pop
    """
233 0834c866 Iustin Pop
    min_percent, max_time, is_degraded, ldisk = self.GetSyncStatus()
234 a8083063 Iustin Pop
    if self._children:
235 a8083063 Iustin Pop
      for child in self._children:
236 0834c866 Iustin Pop
        c_percent, c_time, c_degraded, c_ldisk = child.GetSyncStatus()
237 a8083063 Iustin Pop
        if min_percent is None:
238 a8083063 Iustin Pop
          min_percent = c_percent
239 a8083063 Iustin Pop
        elif c_percent is not None:
240 a8083063 Iustin Pop
          min_percent = min(min_percent, c_percent)
241 a8083063 Iustin Pop
        if max_time is None:
242 a8083063 Iustin Pop
          max_time = c_time
243 a8083063 Iustin Pop
        elif c_time is not None:
244 a8083063 Iustin Pop
          max_time = max(max_time, c_time)
245 a8083063 Iustin Pop
        is_degraded = is_degraded or c_degraded
246 0834c866 Iustin Pop
        ldisk = ldisk or c_ldisk
247 0834c866 Iustin Pop
    return min_percent, max_time, is_degraded, ldisk
248 a8083063 Iustin Pop
249 a8083063 Iustin Pop
250 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
251 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
252 a0c3fea1 Michael Hanselmann

253 a0c3fea1 Michael Hanselmann
    Only supported for some device types.
254 a0c3fea1 Michael Hanselmann

255 a0c3fea1 Michael Hanselmann
    """
256 a0c3fea1 Michael Hanselmann
    for child in self._children:
257 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
258 a0c3fea1 Michael Hanselmann
259 1005d816 Iustin Pop
  def Grow(self, amount):
260 1005d816 Iustin Pop
    """Grow the block device.
261 1005d816 Iustin Pop

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

264 1005d816 Iustin Pop
    """
265 1005d816 Iustin Pop
    raise NotImplementedError
266 a0c3fea1 Michael Hanselmann
267 a8083063 Iustin Pop
  def __repr__(self):
268 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
269 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
270 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
271 a8083063 Iustin Pop
272 a8083063 Iustin Pop
273 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
274 a8083063 Iustin Pop
  """Logical Volume block device.
275 a8083063 Iustin Pop

276 a8083063 Iustin Pop
  """
277 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
278 a8083063 Iustin Pop
    """Attaches to a LV device.
279 a8083063 Iustin Pop

280 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
281 a8083063 Iustin Pop

282 a8083063 Iustin Pop
    """
283 a8083063 Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children)
284 a8083063 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
285 a8083063 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
286 a8083063 Iustin Pop
    self._vg_name, self._lv_name = unique_id
287 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
288 99e8295c Iustin Pop
    self._degraded = True
289 99e8295c Iustin Pop
    self.major = self.minor = None
290 a8083063 Iustin Pop
    self.Attach()
291 a8083063 Iustin Pop
292 a8083063 Iustin Pop
  @classmethod
293 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
294 a8083063 Iustin Pop
    """Create a new logical volume.
295 a8083063 Iustin Pop

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

326 c41eea6e Iustin Pop
    @param vg_name: the volume group name
327 a8083063 Iustin Pop

328 c41eea6e Iustin Pop
    @rtype: list
329 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
330 098c0958 Michael Hanselmann

331 a8083063 Iustin Pop
    """
332 a8083063 Iustin Pop
    command = ["pvs", "--noheadings", "--nosuffix", "--units=m",
333 a8083063 Iustin Pop
               "-opv_name,vg_name,pv_free,pv_attr", "--unbuffered",
334 a8083063 Iustin Pop
               "--separator=:"]
335 a8083063 Iustin Pop
    result = utils.RunCmd(command)
336 a8083063 Iustin Pop
    if result.failed:
337 468c5f77 Iustin Pop
      logging.error("Can't get the PV information: %s - %s",
338 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
339 a8083063 Iustin Pop
      return None
340 a8083063 Iustin Pop
    data = []
341 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
342 a8083063 Iustin Pop
      fields = line.strip().split(':')
343 a8083063 Iustin Pop
      if len(fields) != 4:
344 468c5f77 Iustin Pop
        logging.error("Can't parse pvs output: line '%s'", line)
345 a8083063 Iustin Pop
        return None
346 a8083063 Iustin Pop
      # skip over pvs from another vg or ones which are not allocatable
347 a8083063 Iustin Pop
      if fields[1] != vg_name or fields[3][0] != 'a':
348 a8083063 Iustin Pop
        continue
349 a8083063 Iustin Pop
      data.append((float(fields[2]), fields[0]))
350 a8083063 Iustin Pop
351 a8083063 Iustin Pop
    return data
352 a8083063 Iustin Pop
353 a8083063 Iustin Pop
  def Remove(self):
354 a8083063 Iustin Pop
    """Remove this logical volume.
355 a8083063 Iustin Pop

356 a8083063 Iustin Pop
    """
357 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
358 a8083063 Iustin Pop
      # the LV does not exist
359 a8083063 Iustin Pop
      return True
360 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
361 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
362 a8083063 Iustin Pop
    if result.failed:
363 468c5f77 Iustin Pop
      logging.error("Can't lvremove: %s - %s",
364 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
365 a8083063 Iustin Pop
366 a8083063 Iustin Pop
    return not result.failed
367 a8083063 Iustin Pop
368 f3e513ad Iustin Pop
  def Rename(self, new_id):
369 f3e513ad Iustin Pop
    """Rename this logical volume.
370 f3e513ad Iustin Pop

371 f3e513ad Iustin Pop
    """
372 f3e513ad Iustin Pop
    if not isinstance(new_id, (tuple, list)) or len(new_id) != 2:
373 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Invalid new logical id '%s'" % new_id)
374 f3e513ad Iustin Pop
    new_vg, new_name = new_id
375 f3e513ad Iustin Pop
    if new_vg != self._vg_name:
376 f3e513ad Iustin Pop
      raise errors.ProgrammerError("Can't move a logical volume across"
377 f3e513ad Iustin Pop
                                   " volume groups (from %s to to %s)" %
378 f3e513ad Iustin Pop
                                   (self._vg_name, new_vg))
379 f3e513ad Iustin Pop
    result = utils.RunCmd(["lvrename", new_vg, self._lv_name, new_name])
380 f3e513ad Iustin Pop
    if result.failed:
381 f3e513ad Iustin Pop
      raise errors.BlockDeviceError("Failed to rename the logical volume: %s" %
382 f3e513ad Iustin Pop
                                    result.output)
383 be345db0 Iustin Pop
    self._lv_name = new_name
384 be345db0 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
385 be345db0 Iustin Pop
386 a8083063 Iustin Pop
  def Attach(self):
387 a8083063 Iustin Pop
    """Attach to an existing LV.
388 a8083063 Iustin Pop

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

393 a8083063 Iustin Pop
    """
394 cb999543 Iustin Pop
    self.attached = False
395 99e8295c Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "--separator=,",
396 99e8295c Iustin Pop
                           "-olv_attr,lv_kernel_major,lv_kernel_minor",
397 99e8295c Iustin Pop
                           self.dev_path])
398 a8083063 Iustin Pop
    if result.failed:
399 468c5f77 Iustin Pop
      logging.error("Can't find LV %s: %s, %s",
400 468c5f77 Iustin Pop
                    self.dev_path, result.fail_reason, result.output)
401 a8083063 Iustin Pop
      return False
402 99e8295c Iustin Pop
    out = result.stdout.strip().rstrip(',')
403 99e8295c Iustin Pop
    out = out.split(",")
404 99e8295c Iustin Pop
    if len(out) != 3:
405 468c5f77 Iustin Pop
      logging.error("Can't parse LVS output, len(%s) != 3", str(out))
406 99e8295c Iustin Pop
      return False
407 99e8295c Iustin Pop
408 99e8295c Iustin Pop
    status, major, minor = out[:3]
409 99e8295c Iustin Pop
    if len(status) != 6:
410 468c5f77 Iustin Pop
      logging.error("lvs lv_attr is not 6 characters (%s)", status)
411 99e8295c Iustin Pop
      return False
412 99e8295c Iustin Pop
413 99e8295c Iustin Pop
    try:
414 99e8295c Iustin Pop
      major = int(major)
415 99e8295c Iustin Pop
      minor = int(minor)
416 99e8295c Iustin Pop
    except ValueError, err:
417 468c5f77 Iustin Pop
      logging.error("lvs major/minor cannot be parsed: %s", str(err))
418 99e8295c Iustin Pop
419 99e8295c Iustin Pop
    self.major = major
420 99e8295c Iustin Pop
    self.minor = minor
421 99e8295c Iustin Pop
    self._degraded = status[0] == 'v' # virtual volume, i.e. doesn't backing
422 99e8295c Iustin Pop
                                      # storage
423 cb999543 Iustin Pop
    self.attached = True
424 99e8295c Iustin Pop
    return True
425 a8083063 Iustin Pop
426 a8083063 Iustin Pop
  def Assemble(self):
427 a8083063 Iustin Pop
    """Assemble the device.
428 a8083063 Iustin Pop

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

433 a8083063 Iustin Pop
    """
434 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
435 5574047a Iustin Pop
    if result.failed:
436 468c5f77 Iustin Pop
      logging.error("Can't activate lv %s: %s", self.dev_path, result.output)
437 cb999543 Iustin Pop
      return False
438 cb999543 Iustin Pop
    return self.Attach()
439 a8083063 Iustin Pop
440 a8083063 Iustin Pop
  def Shutdown(self):
441 a8083063 Iustin Pop
    """Shutdown the device.
442 a8083063 Iustin Pop

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

446 a8083063 Iustin Pop
    """
447 a8083063 Iustin Pop
    return True
448 a8083063 Iustin Pop
449 9db6dbce Iustin Pop
  def GetSyncStatus(self):
450 9db6dbce Iustin Pop
    """Returns the sync status of the device.
451 9db6dbce Iustin Pop

452 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
453 9db6dbce Iustin Pop
    status of the mirror.
454 9db6dbce Iustin Pop

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

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

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

468 c41eea6e Iustin Pop
    @rtype: tuple
469 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
470 c41eea6e Iustin Pop

471 9db6dbce Iustin Pop
    """
472 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
473 9db6dbce Iustin Pop
474 a8083063 Iustin Pop
  def Open(self, force=False):
475 a8083063 Iustin Pop
    """Make the device ready for I/O.
476 a8083063 Iustin Pop

477 a8083063 Iustin Pop
    This is a no-op for the LV device type.
478 a8083063 Iustin Pop

479 a8083063 Iustin Pop
    """
480 fdbd668d Iustin Pop
    pass
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
  def Close(self):
483 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
484 a8083063 Iustin Pop

485 a8083063 Iustin Pop
    This is a no-op for the LV device type.
486 a8083063 Iustin Pop

487 a8083063 Iustin Pop
    """
488 fdbd668d Iustin Pop
    pass
489 a8083063 Iustin Pop
490 a8083063 Iustin Pop
  def Snapshot(self, size):
491 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
492 a8083063 Iustin Pop

493 a8083063 Iustin Pop
    """
494 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
495 a8083063 Iustin Pop
496 a8083063 Iustin Pop
    # remove existing snapshot if found
497 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
498 a8083063 Iustin Pop
    snap.Remove()
499 a8083063 Iustin Pop
500 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
501 a8083063 Iustin Pop
    if not pvs_info:
502 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
503 3ecf6786 Iustin Pop
                                    self._vg_name)
504 a8083063 Iustin Pop
    pvs_info.sort()
505 a8083063 Iustin Pop
    pvs_info.reverse()
506 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
507 a8083063 Iustin Pop
    if free_size < size:
508 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
509 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
510 a8083063 Iustin Pop
511 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
512 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
513 a8083063 Iustin Pop
    if result.failed:
514 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s - %s" %
515 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
516 6c896e2f Iustin Pop
                                     result.output))
517 a8083063 Iustin Pop
518 a8083063 Iustin Pop
    return snap_name
519 a8083063 Iustin Pop
520 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
521 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
522 a0c3fea1 Michael Hanselmann

523 a0c3fea1 Michael Hanselmann
    """
524 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
525 a0c3fea1 Michael Hanselmann
526 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
527 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
528 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
529 a0c3fea1 Michael Hanselmann
530 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
531 a0c3fea1 Michael Hanselmann
    text = text[:128]
532 a0c3fea1 Michael Hanselmann
533 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
534 a0c3fea1 Michael Hanselmann
                           self.dev_path])
535 a0c3fea1 Michael Hanselmann
    if result.failed:
536 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s - %s" %
537 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
538 6c896e2f Iustin Pop
                                     result.output))
539 1005d816 Iustin Pop
  def Grow(self, amount):
540 1005d816 Iustin Pop
    """Grow the logical volume.
541 1005d816 Iustin Pop

542 1005d816 Iustin Pop
    """
543 1005d816 Iustin Pop
    # we try multiple algorithms since the 'best' ones might not have
544 1005d816 Iustin Pop
    # space available in the right place, but later ones might (since
545 1005d816 Iustin Pop
    # they have less constraints); also note that only recent LVM
546 1005d816 Iustin Pop
    # supports 'cling'
547 1005d816 Iustin Pop
    for alloc_policy in "contiguous", "cling", "normal":
548 1005d816 Iustin Pop
      result = utils.RunCmd(["lvextend", "--alloc", alloc_policy,
549 1005d816 Iustin Pop
                             "-L", "+%dm" % amount, self.dev_path])
550 1005d816 Iustin Pop
      if not result.failed:
551 1005d816 Iustin Pop
        return
552 1005d816 Iustin Pop
    raise errors.BlockDeviceError("Can't grow LV %s: %s" %
553 1005d816 Iustin Pop
                                  (self.dev_path, result.output))
554 a0c3fea1 Michael Hanselmann
555 a0c3fea1 Michael Hanselmann
556 6b90c22e Iustin Pop
class DRBD8Status(object):
557 6b90c22e Iustin Pop
  """A DRBD status representation class.
558 6b90c22e Iustin Pop

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

561 6b90c22e Iustin Pop
  """
562 6b90c22e Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+st:([^/]+)/(\S+)"
563 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
564 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
565 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
566 6b90c22e Iustin Pop
567 6b90c22e Iustin Pop
  def __init__(self, procline):
568 6b90c22e Iustin Pop
    m = self.LINE_RE.match(procline)
569 6b90c22e Iustin Pop
    if not m:
570 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
571 6b90c22e Iustin Pop
    self.cstatus = m.group(1)
572 6b90c22e Iustin Pop
    self.lrole = m.group(2)
573 6b90c22e Iustin Pop
    self.rrole = m.group(3)
574 6b90c22e Iustin Pop
    self.ldisk = m.group(4)
575 6b90c22e Iustin Pop
    self.rdisk = m.group(5)
576 6b90c22e Iustin Pop
577 6b90c22e Iustin Pop
    self.is_standalone = self.cstatus == "StandAlone"
578 6b90c22e Iustin Pop
    self.is_wfconn = self.cstatus == "WFConnection"
579 6b90c22e Iustin Pop
    self.is_connected = self.cstatus == "Connected"
580 6b90c22e Iustin Pop
    self.is_primary = self.lrole == "Primary"
581 6b90c22e Iustin Pop
    self.is_secondary = self.lrole == "Secondary"
582 6b90c22e Iustin Pop
    self.peer_primary = self.rrole == "Primary"
583 6b90c22e Iustin Pop
    self.peer_secondary = self.rrole == "Secondary"
584 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
585 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
586 6b90c22e Iustin Pop
587 6b90c22e Iustin Pop
    self.is_diskless = self.ldisk == "Diskless"
588 6b90c22e Iustin Pop
    self.is_disk_uptodate = self.ldisk == "UpToDate"
589 6b90c22e Iustin Pop
590 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
591 6b90c22e Iustin Pop
    if m:
592 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
593 6b90c22e Iustin Pop
      hours = int(m.group(2))
594 6b90c22e Iustin Pop
      minutes = int(m.group(3))
595 6b90c22e Iustin Pop
      seconds = int(m.group(4))
596 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
597 6b90c22e Iustin Pop
    else:
598 6b90c22e Iustin Pop
      self.sync_percent = None
599 6b90c22e Iustin Pop
      self.est_time = None
600 6b90c22e Iustin Pop
601 6b90c22e Iustin Pop
    self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget"
602 6b90c22e Iustin Pop
    self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource"
603 6b90c22e Iustin Pop
    self.is_resync = self.is_sync_target or self.is_sync_source
604 6b90c22e Iustin Pop
605 6b90c22e Iustin Pop
606 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
607 0f7f32d9 Iustin Pop
  """Base DRBD class.
608 a8083063 Iustin Pop

609 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
610 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
611 0f7f32d9 Iustin Pop

612 abdf0113 Iustin Pop
  """
613 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
614 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
615 a8083063 Iustin Pop
616 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
617 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
618 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
619 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
620 a8083063 Iustin Pop
621 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
622 6b90c22e Iustin Pop
623 abdf0113 Iustin Pop
  @staticmethod
624 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
625 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
626 a8083063 Iustin Pop

627 a8083063 Iustin Pop
    """
628 6b90c22e Iustin Pop
    stat = open(filename, "r")
629 abdf0113 Iustin Pop
    try:
630 abdf0113 Iustin Pop
      data = stat.read().splitlines()
631 abdf0113 Iustin Pop
    finally:
632 abdf0113 Iustin Pop
      stat.close()
633 abdf0113 Iustin Pop
    if not data:
634 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from %s" % filename)
635 abdf0113 Iustin Pop
    return data
636 a8083063 Iustin Pop
637 abdf0113 Iustin Pop
  @staticmethod
638 abdf0113 Iustin Pop
  def _MassageProcData(data):
639 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
640 a8083063 Iustin Pop

641 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
642 c41eea6e Iustin Pop
        for that minor
643 a8083063 Iustin Pop

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

667 abdf0113 Iustin Pop
    This will return a dict with keys:
668 c41eea6e Iustin Pop
      - k_major
669 c41eea6e Iustin Pop
      - k_minor
670 c41eea6e Iustin Pop
      - k_point
671 c41eea6e Iustin Pop
      - api
672 c41eea6e Iustin Pop
      - proto
673 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
674 a8083063 Iustin Pop

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

699 a8083063 Iustin Pop
    """
700 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
701 a8083063 Iustin Pop
702 abdf0113 Iustin Pop
  @classmethod
703 abdf0113 Iustin Pop
  def _GetUsedDevs(cls):
704 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
705 a8083063 Iustin Pop

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

726 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
727 a8083063 Iustin Pop

728 a8083063 Iustin Pop
    """
729 abdf0113 Iustin Pop
    if minor is None:
730 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
731 cb999543 Iustin Pop
      self.attached = False
732 a8083063 Iustin Pop
    else:
733 abdf0113 Iustin Pop
      self.minor = minor
734 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
735 cb999543 Iustin Pop
      self.attached = True
736 a8083063 Iustin Pop
737 a8083063 Iustin Pop
  @staticmethod
738 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
739 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
740 a8083063 Iustin Pop

741 abdf0113 Iustin Pop
    This currently only check the size, which must be around
742 abdf0113 Iustin Pop
    128MiB.
743 a8083063 Iustin Pop

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

767 abdf0113 Iustin Pop
    This is not supported for drbd devices.
768 a8083063 Iustin Pop

769 a8083063 Iustin Pop
    """
770 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
771 a8083063 Iustin Pop
772 f3e513ad Iustin Pop
773 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
774 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
775 a2cfdea2 Iustin Pop

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

780 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
781 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
782 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
783 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
784 a2cfdea2 Iustin Pop

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

821 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
822 a2cfdea2 Iustin Pop

823 a2cfdea2 Iustin Pop
    """
824 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
825 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
826 a2cfdea2 Iustin Pop
    if result.failed:
827 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
828 a2cfdea2 Iustin Pop
                                    result.output)
829 a2cfdea2 Iustin Pop
830 a2cfdea2 Iustin Pop
  @classmethod
831 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
832 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
833 a2cfdea2 Iustin Pop

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

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

862 a2cfdea2 Iustin Pop
    """
863 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
864 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
865 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
866 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
867 a2cfdea2 Iustin Pop
                           "dstate"])
868 a2cfdea2 Iustin Pop
    if result.failed:
869 468c5f77 Iustin Pop
      logging.error("Invalid meta device %s: %s", meta_device, result.output)
870 a2cfdea2 Iustin Pop
      return False
871 a2cfdea2 Iustin Pop
    return True
872 a2cfdea2 Iustin Pop
873 a2cfdea2 Iustin Pop
  @classmethod
874 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
875 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
876 a2cfdea2 Iustin Pop

877 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
878 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
879 a2cfdea2 Iustin Pop

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

927 a2cfdea2 Iustin Pop
    """
928 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
929 a2cfdea2 Iustin Pop
    if result.failed:
930 468c5f77 Iustin Pop
      logging.error("Can't display the drbd config: %s - %s",
931 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
932 3840729d Iustin Pop
      return None
933 3840729d Iustin Pop
    return result.stdout
934 3840729d Iustin Pop
935 3840729d Iustin Pop
  @classmethod
936 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
937 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
938 3840729d Iustin Pop

939 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
940 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
941 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
942 3840729d Iustin Pop
    by _GetShowData.
943 3840729d Iustin Pop

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

979 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
980 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
981 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
982 a2cfdea2 Iustin Pop
    device.
983 a2cfdea2 Iustin Pop

984 a2cfdea2 Iustin Pop
    """
985 b00b95dd Iustin Pop
    if self._children:
986 b00b95dd Iustin Pop
      backend, meta = self._children
987 b00b95dd Iustin Pop
    else:
988 b00b95dd Iustin Pop
      backend = meta = None
989 b00b95dd Iustin Pop
990 a2cfdea2 Iustin Pop
    if backend is not None:
991 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
992 a2cfdea2 Iustin Pop
    else:
993 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
994 b00b95dd Iustin Pop
995 a2cfdea2 Iustin Pop
    if meta is not None:
996 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
997 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
998 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
999 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
1000 a2cfdea2 Iustin Pop
    else:
1001 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
1002 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
1003 a2cfdea2 Iustin Pop
    return retval
1004 a2cfdea2 Iustin Pop
1005 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
1006 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
1007 a2cfdea2 Iustin Pop

1008 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
1009 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
1010 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
1011 a2cfdea2 Iustin Pop
    device.
1012 a2cfdea2 Iustin Pop

1013 a2cfdea2 Iustin Pop
    """
1014 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
1015 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
1016 a2cfdea2 Iustin Pop
      return True
1017 a2cfdea2 Iustin Pop
1018 a2cfdea2 Iustin Pop
    if self._lhost is None:
1019 a2cfdea2 Iustin Pop
      return False
1020 a2cfdea2 Iustin Pop
1021 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
1022 a2cfdea2 Iustin Pop
            "remote_addr" in info):
1023 a2cfdea2 Iustin Pop
      return False
1024 a2cfdea2 Iustin Pop
1025 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
1026 a2cfdea2 Iustin Pop
    retval = (retval and
1027 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
1028 a2cfdea2 Iustin Pop
    return retval
1029 a2cfdea2 Iustin Pop
1030 a2cfdea2 Iustin Pop
  @classmethod
1031 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
1032 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
1033 a2cfdea2 Iustin Pop

1034 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
1035 a2cfdea2 Iustin Pop
    device. And it must be done only once.
1036 a2cfdea2 Iustin Pop

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

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

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

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

1160 7d585316 Iustin Pop
    This is the low-level implementation.
1161 7d585316 Iustin Pop

1162 7d585316 Iustin Pop
    @type minor: int
1163 7d585316 Iustin Pop
    @param minor: the drbd minor whose settings we change
1164 7d585316 Iustin Pop
    @type kbytes: int
1165 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1166 7d585316 Iustin Pop
    @rtype: boolean
1167 7d585316 Iustin Pop
    @return: the success of the operation
1168 7d585316 Iustin Pop

1169 a2cfdea2 Iustin Pop
    """
1170 7d585316 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "syncer",
1171 7d585316 Iustin Pop
                           "-r", "%d" % kbytes, "--create-device"])
1172 a2cfdea2 Iustin Pop
    if result.failed:
1173 468c5f77 Iustin Pop
      logging.error("Can't change syncer rate: %s - %s",
1174 468c5f77 Iustin Pop
                    result.fail_reason, result.output)
1175 7d585316 Iustin Pop
    return not result.failed
1176 7d585316 Iustin Pop
1177 7d585316 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1178 7d585316 Iustin Pop
    """Set the speed of the DRBD syncer.
1179 7d585316 Iustin Pop

1180 7d585316 Iustin Pop
    @type kbytes: int
1181 7d585316 Iustin Pop
    @param kbytes: the speed in kbytes/second
1182 7d585316 Iustin Pop
    @rtype: boolean
1183 7d585316 Iustin Pop
    @return: the success of the operation
1184 7d585316 Iustin Pop

1185 7d585316 Iustin Pop
    """
1186 7d585316 Iustin Pop
    if self.minor is None:
1187 7d585316 Iustin Pop
      logging.info("Not attached during SetSyncSpeed")
1188 7d585316 Iustin Pop
      return False
1189 7d585316 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1190 7d585316 Iustin Pop
    return self._SetMinorSyncSpeed(self.minor, kbytes) and children_result
1191 a2cfdea2 Iustin Pop
1192 6b90c22e Iustin Pop
  def GetProcStatus(self):
1193 6b90c22e Iustin Pop
    """Return device data from /proc.
1194 6b90c22e Iustin Pop

1195 6b90c22e Iustin Pop
    """
1196 6b90c22e Iustin Pop
    if self.minor is None:
1197 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("GetStats() called while not attached")
1198 6b90c22e Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1199 6b90c22e Iustin Pop
    if self.minor not in proc_info:
1200 6b90c22e Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1201 6b90c22e Iustin Pop
                                    self.minor)
1202 6b90c22e Iustin Pop
    return DRBD8Status(proc_info[self.minor])
1203 6b90c22e Iustin Pop
1204 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1205 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1206 a2cfdea2 Iustin Pop

1207 a2cfdea2 Iustin Pop

1208 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1209 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1210 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1211 0834c866 Iustin Pop

1212 0834c866 Iustin Pop

1213 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1214 0834c866 Iustin Pop
    network not connected or local disk missing.
1215 0834c866 Iustin Pop

1216 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1217 0834c866 Iustin Pop
    disk or not.
1218 a2cfdea2 Iustin Pop

1219 c41eea6e Iustin Pop
    @rtype: tuple
1220 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
1221 c41eea6e Iustin Pop

1222 a2cfdea2 Iustin Pop
    """
1223 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1224 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1225 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1226 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1227 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1228 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1229 a2cfdea2 Iustin Pop
1230 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1231 a2cfdea2 Iustin Pop
    """Make the local state primary.
1232 a2cfdea2 Iustin Pop

1233 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1234 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1235 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1236 f860ff4e Guido Trotter
    is mandatory.
1237 a2cfdea2 Iustin Pop

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

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

1256 a2cfdea2 Iustin Pop
    """
1257 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1258 468c5f77 Iustin Pop
      logging.info("Instance not attached to a device")
1259 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1260 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1261 a2cfdea2 Iustin Pop
    if result.failed:
1262 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1263 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1264 468c5f77 Iustin Pop
      logging.error(msg)
1265 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1266 a2cfdea2 Iustin Pop
1267 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1268 cf8df3f3 Iustin Pop
    """Removes network configuration.
1269 cf8df3f3 Iustin Pop

1270 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1271 cf8df3f3 Iustin Pop

1272 cf8df3f3 Iustin Pop
    The method will wait up to a hardcoded timeout for the device to
1273 cf8df3f3 Iustin Pop
    go into standalone after the 'disconnect' command before
1274 cf8df3f3 Iustin Pop
    re-configuring it, as sometimes it takes a while for the
1275 cf8df3f3 Iustin Pop
    disconnect to actually propagate and thus we might issue a 'net'
1276 cf8df3f3 Iustin Pop
    command while the device is still connected. If the device will
1277 cf8df3f3 Iustin Pop
    still be attached to the network and we time out, we raise an
1278 cf8df3f3 Iustin Pop
    exception.
1279 cf8df3f3 Iustin Pop

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

1318 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1319 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1320 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1321 cf8df3f3 Iustin Pop

1322 cf8df3f3 Iustin Pop
    Args:
1323 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1324 cf8df3f3 Iustin Pop

1325 cf8df3f3 Iustin Pop
    """
1326 cf8df3f3 Iustin Pop
    if self.minor is None:
1327 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk not attached in AttachNet")
1328 cf8df3f3 Iustin Pop
1329 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1330 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("DRBD disk missing network info in"
1331 cf8df3f3 Iustin Pop
                                    " AttachNet()")
1332 cf8df3f3 Iustin Pop
1333 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1334 cf8df3f3 Iustin Pop
1335 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1336 cf8df3f3 Iustin Pop
      raise errors.BlockDeviceError("Device is not standalone in AttachNet")
1337 cf8df3f3 Iustin Pop
1338 cf8df3f3 Iustin Pop
    return self._AssembleNet(self.minor,
1339 cf8df3f3 Iustin Pop
                             (self._lhost, self._lport,
1340 cf8df3f3 Iustin Pop
                              self._rhost, self._rport),
1341 cf8df3f3 Iustin Pop
                             "C", dual_pri=multimaster)
1342 cf8df3f3 Iustin Pop
1343 a2cfdea2 Iustin Pop
  def Attach(self):
1344 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1345 a2cfdea2 Iustin Pop

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

1350 a2cfdea2 Iustin Pop
    """
1351 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1352 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1353 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1354 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1355 a2cfdea2 Iustin Pop
      if match_l and match_r:
1356 a2cfdea2 Iustin Pop
        break
1357 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1358 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
1359 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
1360 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
1361 3c03759a Iustin Pop
                                  constants.DRBD_NET_PROTOCOL,
1362 3c03759a Iustin Pop
                                  hmac=constants.DRBD_HMAC_ALG,
1363 2899d9de Iustin Pop
                                  secret=self._secret
1364 2899d9de Iustin Pop
                                  )
1365 3840729d Iustin Pop
        if res_r:
1366 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1367 3840729d Iustin Pop
            break
1368 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
1369 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
1370 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1371 fc1dc9d7 Iustin Pop
        break
1372 bf25af3b Iustin Pop
1373 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1374 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1375 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1376 bf25af3b Iustin Pop
      # configuration
1377 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1378 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1379 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1380 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1381 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1382 9cdbe77f Iustin Pop
        # reconnect to our correct one
1383 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
1384 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
1385 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
1386 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
1387 9cdbe77f Iustin Pop
                                        " the correct peer")
1388 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1389 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1390 9cdbe77f Iustin Pop
        # None)
1391 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
1392 3c03759a Iustin Pop
                                      self._rhost, self._rport),
1393 3c03759a Iustin Pop
                              constants.DRBD_NET_PROTOCOL,
1394 2899d9de Iustin Pop
                              hmac=constants.DRBD_HMAC_ALG,
1395 2899d9de Iustin Pop
                              secret=self._secret) and
1396 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
1397 9cdbe77f Iustin Pop
          break
1398 9cdbe77f Iustin Pop
1399 a2cfdea2 Iustin Pop
    else:
1400 a2cfdea2 Iustin Pop
      minor = None
1401 a2cfdea2 Iustin Pop
1402 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1403 a2cfdea2 Iustin Pop
    return minor is not None
1404 a2cfdea2 Iustin Pop
1405 a2cfdea2 Iustin Pop
  def Assemble(self):
1406 a2cfdea2 Iustin Pop
    """Assemble the drbd.
1407 a2cfdea2 Iustin Pop

1408 a2cfdea2 Iustin Pop
    Method:
1409 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
1410 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
1411 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
1412 a2cfdea2 Iustin Pop
        - if yes, abort?
1413 a2cfdea2 Iustin Pop
        - if not, bind
1414 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
1415 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
1416 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
1417 a2cfdea2 Iustin Pop
          if yes abort
1418 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
1419 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
1420 a2cfdea2 Iustin Pop
        - bind our net port
1421 a2cfdea2 Iustin Pop
        - bind the remote net port
1422 a2cfdea2 Iustin Pop

1423 a2cfdea2 Iustin Pop
    """
1424 a2cfdea2 Iustin Pop
    self.Attach()
1425 a2cfdea2 Iustin Pop
    if self.minor is not None:
1426 468c5f77 Iustin Pop
      logging.info("Already assembled")
1427 a2cfdea2 Iustin Pop
      return True
1428 a2cfdea2 Iustin Pop
1429 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
1430 a2cfdea2 Iustin Pop
    if not result:
1431 a2cfdea2 Iustin Pop
      return result
1432 a2cfdea2 Iustin Pop
1433 a1578d63 Iustin Pop
    # TODO: maybe completely tear-down the minor (drbdsetup ... down)
1434 a1578d63 Iustin Pop
    # before attaching our own?
1435 a1578d63 Iustin Pop
    minor = self._aminor
1436 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1437 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1438 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1439 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1440 a2cfdea2 Iustin Pop
      if not result:
1441 a2cfdea2 Iustin Pop
        return False
1442 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
1443 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1444 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1445 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1446 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1447 3c03759a Iustin Pop
                                 constants.DRBD_NET_PROTOCOL,
1448 3c03759a Iustin Pop
                                 hmac=constants.DRBD_HMAC_ALG,
1449 2899d9de Iustin Pop
                                 secret=self._secret)
1450 a2cfdea2 Iustin Pop
      if not result:
1451 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
1452 a2cfdea2 Iustin Pop
          # we will ignore failures from this
1453 468c5f77 Iustin Pop
          logging.error("net setup failed, tearing down local device")
1454 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
1455 a2cfdea2 Iustin Pop
        return False
1456 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1457 a2cfdea2 Iustin Pop
    return True
1458 a2cfdea2 Iustin Pop
1459 a2cfdea2 Iustin Pop
  @classmethod
1460 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1461 b00b95dd Iustin Pop
    """Detach from the local device.
1462 b00b95dd Iustin Pop

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

1466 b00b95dd Iustin Pop
    """
1467 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1468 b00b95dd Iustin Pop
    if result.failed:
1469 468c5f77 Iustin Pop
      logging.error("Can't detach local device: %s", result.output)
1470 b00b95dd Iustin Pop
    return not result.failed
1471 b00b95dd Iustin Pop
1472 b00b95dd Iustin Pop
  @classmethod
1473 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1474 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1475 f3e513ad Iustin Pop

1476 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1477 f3e513ad Iustin Pop

1478 f3e513ad Iustin Pop
    """
1479 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1480 a8459f1c Iustin Pop
    if result.failed:
1481 468c5f77 Iustin Pop
      logging.error("Can't shutdown network: %s", result.output)
1482 f3e513ad Iustin Pop
    return not result.failed
1483 f3e513ad Iustin Pop
1484 f3e513ad Iustin Pop
  @classmethod
1485 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1486 a2cfdea2 Iustin Pop
    """Deactivate the device.
1487 a2cfdea2 Iustin Pop

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

1490 a2cfdea2 Iustin Pop
    """
1491 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1492 a2cfdea2 Iustin Pop
    if result.failed:
1493 468c5f77 Iustin Pop
      logging.error("Can't shutdown drbd device: %s", result.output)
1494 a2cfdea2 Iustin Pop
    return not result.failed
1495 a2cfdea2 Iustin Pop
1496 a2cfdea2 Iustin Pop
  def Shutdown(self):
1497 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1498 a2cfdea2 Iustin Pop

1499 a2cfdea2 Iustin Pop
    """
1500 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1501 468c5f77 Iustin Pop
      logging.info("DRBD device not attached to a device during Shutdown")
1502 a2cfdea2 Iustin Pop
      return True
1503 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
1504 a2cfdea2 Iustin Pop
      return False
1505 a2cfdea2 Iustin Pop
    self.minor = None
1506 a2cfdea2 Iustin Pop
    self.dev_path = None
1507 a2cfdea2 Iustin Pop
    return True
1508 a2cfdea2 Iustin Pop
1509 a2cfdea2 Iustin Pop
  def Remove(self):
1510 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1511 a2cfdea2 Iustin Pop

1512 a2cfdea2 Iustin Pop
    """
1513 a2cfdea2 Iustin Pop
    return self.Shutdown()
1514 a2cfdea2 Iustin Pop
1515 a2cfdea2 Iustin Pop
  @classmethod
1516 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1517 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1518 a2cfdea2 Iustin Pop

1519 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1520 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1521 a2cfdea2 Iustin Pop

1522 a2cfdea2 Iustin Pop
    """
1523 a2cfdea2 Iustin Pop
    if len(children) != 2:
1524 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1525 a2cfdea2 Iustin Pop
    meta = children[1]
1526 a2cfdea2 Iustin Pop
    meta.Assemble()
1527 a2cfdea2 Iustin Pop
    if not meta.Attach():
1528 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1529 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1530 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1531 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
1532 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1533 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1534 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1535 a2cfdea2 Iustin Pop
1536 1005d816 Iustin Pop
  def Grow(self, amount):
1537 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1538 1005d816 Iustin Pop

1539 1005d816 Iustin Pop
    """
1540 1005d816 Iustin Pop
    if self.minor is None:
1541 1005d816 Iustin Pop
      raise errors.ProgrammerError("drbd8: Grow called while not attached")
1542 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1543 1005d816 Iustin Pop
      raise errors.BlockDeviceError("Cannot grow diskless DRBD8 device")
1544 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1545 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1546 1005d816 Iustin Pop
    if result.failed:
1547 1005d816 Iustin Pop
      raise errors.BlockDeviceError("resize failed for %s: %s" %
1548 1005d816 Iustin Pop
                                    (self.dev_path, result.output))
1549 1005d816 Iustin Pop
    return
1550 1005d816 Iustin Pop
1551 a8083063 Iustin Pop
1552 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1553 6f695a2e Manuel Franceschini
  """File device.
1554 abdf0113 Iustin Pop

1555 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1556 6f695a2e Manuel Franceschini

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

1559 6f695a2e Manuel Franceschini
  """
1560 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1561 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1562 6f695a2e Manuel Franceschini

1563 6f695a2e Manuel Franceschini
    """
1564 6f695a2e Manuel Franceschini
    if children:
1565 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1566 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
1567 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1568 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1569 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1570 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1571 ecb091e3 Iustin Pop
    self.Attach()
1572 6f695a2e Manuel Franceschini
1573 6f695a2e Manuel Franceschini
  def Assemble(self):
1574 6f695a2e Manuel Franceschini
    """Assemble the device.
1575 6f695a2e Manuel Franceschini

1576 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1577 6f695a2e Manuel Franceschini

1578 6f695a2e Manuel Franceschini
    """
1579 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1580 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1581 6f695a2e Manuel Franceschini
                                    self.dev_path)
1582 6f695a2e Manuel Franceschini
    return True
1583 6f695a2e Manuel Franceschini
1584 6f695a2e Manuel Franceschini
  def Shutdown(self):
1585 6f695a2e Manuel Franceschini
    """Shutdown the device.
1586 6f695a2e Manuel Franceschini

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

1590 6f695a2e Manuel Franceschini
    """
1591 6f695a2e Manuel Franceschini
    return True
1592 6f695a2e Manuel Franceschini
1593 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1594 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1595 6f695a2e Manuel Franceschini

1596 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1597 6f695a2e Manuel Franceschini

1598 6f695a2e Manuel Franceschini
    """
1599 6f695a2e Manuel Franceschini
    pass
1600 6f695a2e Manuel Franceschini
1601 6f695a2e Manuel Franceschini
  def Close(self):
1602 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1603 6f695a2e Manuel Franceschini

1604 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1605 6f695a2e Manuel Franceschini

1606 6f695a2e Manuel Franceschini
    """
1607 6f695a2e Manuel Franceschini
    pass
1608 6f695a2e Manuel Franceschini
1609 6f695a2e Manuel Franceschini
  def Remove(self):
1610 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1611 6f695a2e Manuel Franceschini

1612 c41eea6e Iustin Pop
    @rtype: boolean
1613 c41eea6e Iustin Pop
    @return: True if the removal was successful
1614 6f695a2e Manuel Franceschini

1615 6f695a2e Manuel Franceschini
    """
1616 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1617 6f695a2e Manuel Franceschini
      return True
1618 6f695a2e Manuel Franceschini
    try:
1619 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1620 6f695a2e Manuel Franceschini
      return True
1621 6f695a2e Manuel Franceschini
    except OSError, err:
1622 468c5f77 Iustin Pop
      logging.error("Can't remove file '%s': %s", self.dev_path, err)
1623 6f695a2e Manuel Franceschini
      return False
1624 6f695a2e Manuel Franceschini
1625 6f695a2e Manuel Franceschini
  def Attach(self):
1626 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1627 6f695a2e Manuel Franceschini

1628 6f695a2e Manuel Franceschini
    Check if this file already exists.
1629 6f695a2e Manuel Franceschini

1630 c41eea6e Iustin Pop
    @rtype: boolean
1631 c41eea6e Iustin Pop
    @return: True if file exists
1632 6f695a2e Manuel Franceschini

1633 6f695a2e Manuel Franceschini
    """
1634 ecb091e3 Iustin Pop
    self.attached = os.path.exists(self.dev_path)
1635 ecb091e3 Iustin Pop
    return self.attached
1636 6f695a2e Manuel Franceschini
1637 6f695a2e Manuel Franceschini
  @classmethod
1638 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1639 6f695a2e Manuel Franceschini
    """Create a new file.
1640 6f695a2e Manuel Franceschini

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

1643 c41eea6e Iustin Pop
    @rtype: L{bdev.FileStorage}
1644 c41eea6e Iustin Pop
    @return: an instance of FileStorage
1645 6f695a2e Manuel Franceschini

1646 6f695a2e Manuel Franceschini
    """
1647 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1648 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1649 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1650 6f695a2e Manuel Franceschini
    try:
1651 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1652 6f695a2e Manuel Franceschini
    except IOError, err:
1653 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
1654 6f695a2e Manuel Franceschini
    else:
1655 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1656 6f695a2e Manuel Franceschini
      f.close()
1657 6f695a2e Manuel Franceschini
1658 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
1659 6f695a2e Manuel Franceschini
1660 6f695a2e Manuel Franceschini
1661 a8083063 Iustin Pop
DEV_MAP = {
1662 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1663 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1664 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1665 a8083063 Iustin Pop
  }
1666 a8083063 Iustin Pop
1667 a8083063 Iustin Pop
1668 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1669 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1670 a8083063 Iustin Pop

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

1674 a8083063 Iustin Pop
  """
1675 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1676 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1677 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1678 cb999543 Iustin Pop
  if not device.attached:
1679 a8083063 Iustin Pop
    return None
1680 ecb091e3 Iustin Pop
  return device
1681 a8083063 Iustin Pop
1682 a8083063 Iustin Pop
1683 a8083063 Iustin Pop
def AttachOrAssemble(dev_type, unique_id, children):
1684 a8083063 Iustin Pop
  """Try to attach or assemble an existing device.
1685 a8083063 Iustin Pop

1686 a8083063 Iustin Pop
  This will attach to an existing assembled device or will assemble
1687 a8083063 Iustin Pop
  the device, as needed, to bring it fully up.
1688 a8083063 Iustin Pop

1689 a8083063 Iustin Pop
  """
1690 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1691 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1692 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1693 cb999543 Iustin Pop
  if not device.attached:
1694 a8083063 Iustin Pop
    device.Assemble()
1695 cb999543 Iustin Pop
    if not device.attached:
1696 be1ba2bd Iustin Pop
      raise errors.BlockDeviceError("Can't find a valid block device for"
1697 be1ba2bd Iustin Pop
                                    " %s/%s/%s" %
1698 be1ba2bd Iustin Pop
                                    (dev_type, unique_id, children))
1699 a8083063 Iustin Pop
  return device
1700 a8083063 Iustin Pop
1701 a8083063 Iustin Pop
1702 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1703 a8083063 Iustin Pop
  """Create a device.
1704 a8083063 Iustin Pop

1705 a8083063 Iustin Pop
  """
1706 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1707 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1708 a8083063 Iustin Pop
  device = DEV_MAP[dev_type].Create(unique_id, children, size)
1709 a8083063 Iustin Pop
  return device