Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ f069addf

History | View | Annotate | Download (53.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

277 1005d816 Iustin Pop
    """
278 1005d816 Iustin Pop
    raise NotImplementedError
279 a0c3fea1 Michael Hanselmann
280 a8083063 Iustin Pop
  def __repr__(self):
281 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
282 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
283 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
284 a8083063 Iustin Pop
285 a8083063 Iustin Pop
286 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
287 a8083063 Iustin Pop
  """Logical Volume block device.
288 a8083063 Iustin Pop

289 a8083063 Iustin Pop
  """
290 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
291 a8083063 Iustin Pop
    """Attaches to a LV device.
292 a8083063 Iustin Pop

293 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
294 a8083063 Iustin Pop

295 a8083063 Iustin Pop
    """
296 464f8daf Iustin Pop
    super(LogicalVolume, self).__init__(unique_id, children, size)
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
    self._vg_name, self._lv_name = unique_id
300 a8083063 Iustin Pop
    self.dev_path = "/dev/%s/%s" % (self._vg_name, self._lv_name)
301 99e8295c Iustin Pop
    self._degraded = True
302 99e8295c Iustin Pop
    self.major = self.minor = None
303 a8083063 Iustin Pop
    self.Attach()
304 a8083063 Iustin Pop
305 a8083063 Iustin Pop
  @classmethod
306 a8083063 Iustin Pop
  def Create(cls, unique_id, children, size):
307 a8083063 Iustin Pop
    """Create a new logical volume.
308 a8083063 Iustin Pop

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

339 c41eea6e Iustin Pop
    @param vg_name: the volume group name
340 a8083063 Iustin Pop

341 c41eea6e Iustin Pop
    @rtype: list
342 c41eea6e Iustin Pop
    @return: list of tuples (free_space, name) with free_space in mebibytes
343 098c0958 Michael Hanselmann

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

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

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

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

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

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

442 a8083063 Iustin Pop
    """
443 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
444 5574047a Iustin Pop
    if result.failed:
445 1063abd1 Iustin Pop
      _ThrowError("Can't activate lv %s: %s", self.dev_path, result.output)
446 a8083063 Iustin Pop
447 a8083063 Iustin Pop
  def Shutdown(self):
448 a8083063 Iustin Pop
    """Shutdown the device.
449 a8083063 Iustin Pop

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

453 a8083063 Iustin Pop
    """
454 746f7476 Iustin Pop
    pass
455 a8083063 Iustin Pop
456 9db6dbce Iustin Pop
  def GetSyncStatus(self):
457 9db6dbce Iustin Pop
    """Returns the sync status of the device.
458 9db6dbce Iustin Pop

459 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
460 9db6dbce Iustin Pop
    status of the mirror.
461 9db6dbce Iustin Pop

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

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

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

475 c41eea6e Iustin Pop
    @rtype: tuple
476 c41eea6e Iustin Pop
    @return: (sync_percent, estimated_time, is_degraded, ldisk)
477 c41eea6e Iustin Pop

478 9db6dbce Iustin Pop
    """
479 99e8295c Iustin Pop
    return None, None, self._degraded, self._degraded
480 9db6dbce Iustin Pop
481 a8083063 Iustin Pop
  def Open(self, force=False):
482 a8083063 Iustin Pop
    """Make the device ready for I/O.
483 a8083063 Iustin Pop

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

486 a8083063 Iustin Pop
    """
487 fdbd668d Iustin Pop
    pass
488 a8083063 Iustin Pop
489 a8083063 Iustin Pop
  def Close(self):
490 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
491 a8083063 Iustin Pop

492 a8083063 Iustin Pop
    This is a no-op for the LV device type.
493 a8083063 Iustin Pop

494 a8083063 Iustin Pop
    """
495 fdbd668d Iustin Pop
    pass
496 a8083063 Iustin Pop
497 a8083063 Iustin Pop
  def Snapshot(self, size):
498 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
499 a8083063 Iustin Pop

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

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

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

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

565 6b90c22e Iustin Pop
  """
566 767d52d3 Iustin Pop
  UNCONF_RE = re.compile(r"\s*[0-9]+:\s*cs:Unconfigured$")
567 01e2ce3a Iustin Pop
  LINE_RE = re.compile(r"\s*[0-9]+:\s*cs:(\S+)\s+(?:st|ro):([^/]+)/(\S+)"
568 6b90c22e Iustin Pop
                       "\s+ds:([^/]+)/(\S+)\s+.*$")
569 6b90c22e Iustin Pop
  SYNC_RE = re.compile(r"^.*\ssync'ed:\s*([0-9.]+)%.*"
570 6b90c22e Iustin Pop
                       "\sfinish: ([0-9]+):([0-9]+):([0-9]+)\s.*$")
571 6b90c22e Iustin Pop
572 6b90c22e Iustin Pop
  def __init__(self, procline):
573 767d52d3 Iustin Pop
    u = self.UNCONF_RE.match(procline)
574 767d52d3 Iustin Pop
    if u:
575 767d52d3 Iustin Pop
      self.cstatus = "Unconfigured"
576 767d52d3 Iustin Pop
      self.lrole = self.rrole = self.ldisk = self.rdisk = None
577 767d52d3 Iustin Pop
    else:
578 767d52d3 Iustin Pop
      m = self.LINE_RE.match(procline)
579 767d52d3 Iustin Pop
      if not m:
580 767d52d3 Iustin Pop
        raise errors.BlockDeviceError("Can't parse input data '%s'" % procline)
581 767d52d3 Iustin Pop
      self.cstatus = m.group(1)
582 767d52d3 Iustin Pop
      self.lrole = m.group(2)
583 767d52d3 Iustin Pop
      self.rrole = m.group(3)
584 767d52d3 Iustin Pop
      self.ldisk = m.group(4)
585 767d52d3 Iustin Pop
      self.rdisk = m.group(5)
586 767d52d3 Iustin Pop
587 767d52d3 Iustin Pop
    # end reading of data from the LINE_RE or UNCONF_RE
588 6b90c22e Iustin Pop
589 6b90c22e Iustin Pop
    self.is_standalone = self.cstatus == "StandAlone"
590 6b90c22e Iustin Pop
    self.is_wfconn = self.cstatus == "WFConnection"
591 6b90c22e Iustin Pop
    self.is_connected = self.cstatus == "Connected"
592 6b90c22e Iustin Pop
    self.is_primary = self.lrole == "Primary"
593 6b90c22e Iustin Pop
    self.is_secondary = self.lrole == "Secondary"
594 6b90c22e Iustin Pop
    self.peer_primary = self.rrole == "Primary"
595 6b90c22e Iustin Pop
    self.peer_secondary = self.rrole == "Secondary"
596 6b90c22e Iustin Pop
    self.both_primary = self.is_primary and self.peer_primary
597 6b90c22e Iustin Pop
    self.both_secondary = self.is_secondary and self.peer_secondary
598 6b90c22e Iustin Pop
599 6b90c22e Iustin Pop
    self.is_diskless = self.ldisk == "Diskless"
600 6b90c22e Iustin Pop
    self.is_disk_uptodate = self.ldisk == "UpToDate"
601 6b90c22e Iustin Pop
602 767d52d3 Iustin Pop
    self.is_in_resync = self.cstatus in ("SyncSource", "SyncTarget")
603 767d52d3 Iustin Pop
    self.is_in_use = self.cstatus != "Unconfigured"
604 6b93ec9d Iustin Pop
605 6b90c22e Iustin Pop
    m = self.SYNC_RE.match(procline)
606 6b90c22e Iustin Pop
    if m:
607 6b90c22e Iustin Pop
      self.sync_percent = float(m.group(1))
608 6b90c22e Iustin Pop
      hours = int(m.group(2))
609 6b90c22e Iustin Pop
      minutes = int(m.group(3))
610 6b90c22e Iustin Pop
      seconds = int(m.group(4))
611 6b90c22e Iustin Pop
      self.est_time = hours * 3600 + minutes * 60 + seconds
612 6b90c22e Iustin Pop
    else:
613 6b90c22e Iustin Pop
      self.sync_percent = None
614 6b90c22e Iustin Pop
      self.est_time = None
615 6b90c22e Iustin Pop
616 6b90c22e Iustin Pop
    self.is_sync_target = self.peer_sync_source = self.cstatus == "SyncTarget"
617 6b90c22e Iustin Pop
    self.peer_sync_target = self.is_sync_source = self.cstatus == "SyncSource"
618 6b90c22e Iustin Pop
    self.is_resync = self.is_sync_target or self.is_sync_source
619 6b90c22e Iustin Pop
620 6b90c22e Iustin Pop
621 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
622 0f7f32d9 Iustin Pop
  """Base DRBD class.
623 a8083063 Iustin Pop

624 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
625 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
626 0f7f32d9 Iustin Pop

627 abdf0113 Iustin Pop
  """
628 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
629 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
630 a8083063 Iustin Pop
631 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
632 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
633 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
634 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
635 a8083063 Iustin Pop
636 6b90c22e Iustin Pop
  _STATUS_FILE = "/proc/drbd"
637 6b90c22e Iustin Pop
638 abdf0113 Iustin Pop
  @staticmethod
639 6b90c22e Iustin Pop
  def _GetProcData(filename=_STATUS_FILE):
640 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
641 a8083063 Iustin Pop

642 a8083063 Iustin Pop
    """
643 abdf0113 Iustin Pop
    try:
644 f6eaed12 Iustin Pop
      stat = open(filename, "r")
645 f6eaed12 Iustin Pop
      try:
646 f6eaed12 Iustin Pop
        data = stat.read().splitlines()
647 f6eaed12 Iustin Pop
      finally:
648 f6eaed12 Iustin Pop
        stat.close()
649 f6eaed12 Iustin Pop
    except EnvironmentError, err:
650 f6eaed12 Iustin Pop
      if err.errno == errno.ENOENT:
651 f6eaed12 Iustin Pop
        _ThrowError("The file %s cannot be opened, check if the module"
652 f6eaed12 Iustin Pop
                    " is loaded (%s)", filename, str(err))
653 f6eaed12 Iustin Pop
      else:
654 f6eaed12 Iustin Pop
        _ThrowError("Can't read the DRBD proc file %s: %s", filename, str(err))
655 abdf0113 Iustin Pop
    if not data:
656 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
657 abdf0113 Iustin Pop
    return data
658 a8083063 Iustin Pop
659 abdf0113 Iustin Pop
  @staticmethod
660 abdf0113 Iustin Pop
  def _MassageProcData(data):
661 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
662 a8083063 Iustin Pop

663 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
664 c41eea6e Iustin Pop
        for that minor
665 a8083063 Iustin Pop

666 a8083063 Iustin Pop
    """
667 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
668 abdf0113 Iustin Pop
    results = {}
669 abdf0113 Iustin Pop
    old_minor = old_line = None
670 abdf0113 Iustin Pop
    for line in data:
671 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
672 abdf0113 Iustin Pop
      if lresult is not None:
673 abdf0113 Iustin Pop
        if old_minor is not None:
674 abdf0113 Iustin Pop
          results[old_minor] = old_line
675 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
676 abdf0113 Iustin Pop
        old_line = line
677 abdf0113 Iustin Pop
      else:
678 abdf0113 Iustin Pop
        if old_minor is not None:
679 abdf0113 Iustin Pop
          old_line += " " + line.strip()
680 abdf0113 Iustin Pop
    # add last line
681 abdf0113 Iustin Pop
    if old_minor is not None:
682 abdf0113 Iustin Pop
      results[old_minor] = old_line
683 abdf0113 Iustin Pop
    return results
684 a8083063 Iustin Pop
685 abdf0113 Iustin Pop
  @classmethod
686 abdf0113 Iustin Pop
  def _GetVersion(cls):
687 abdf0113 Iustin Pop
    """Return the DRBD version.
688 a8083063 Iustin Pop

689 abdf0113 Iustin Pop
    This will return a dict with keys:
690 c41eea6e Iustin Pop
      - k_major
691 c41eea6e Iustin Pop
      - k_minor
692 c41eea6e Iustin Pop
      - k_point
693 c41eea6e Iustin Pop
      - api
694 c41eea6e Iustin Pop
      - proto
695 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
696 a8083063 Iustin Pop

697 a8083063 Iustin Pop
    """
698 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
699 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
700 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
701 abdf0113 Iustin Pop
    if not version:
702 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
703 abdf0113 Iustin Pop
                                    first_line)
704 a8083063 Iustin Pop
705 abdf0113 Iustin Pop
    values = version.groups()
706 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
707 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
708 abdf0113 Iustin Pop
              'k_point': int(values[2]),
709 abdf0113 Iustin Pop
              'api': int(values[3]),
710 abdf0113 Iustin Pop
              'proto': int(values[4]),
711 abdf0113 Iustin Pop
             }
712 abdf0113 Iustin Pop
    if values[5] is not None:
713 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
714 a8083063 Iustin Pop
715 abdf0113 Iustin Pop
    return retval
716 abdf0113 Iustin Pop
717 abdf0113 Iustin Pop
  @staticmethod
718 abdf0113 Iustin Pop
  def _DevPath(minor):
719 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
720 a8083063 Iustin Pop

721 a8083063 Iustin Pop
    """
722 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
723 a8083063 Iustin Pop
724 abdf0113 Iustin Pop
  @classmethod
725 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
726 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
727 a8083063 Iustin Pop

728 a8083063 Iustin Pop
    """
729 abdf0113 Iustin Pop
    data = cls._GetProcData()
730 a8083063 Iustin Pop
731 abdf0113 Iustin Pop
    used_devs = {}
732 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
733 abdf0113 Iustin Pop
    for line in data:
734 abdf0113 Iustin Pop
      match = valid_line.match(line)
735 abdf0113 Iustin Pop
      if not match:
736 abdf0113 Iustin Pop
        continue
737 abdf0113 Iustin Pop
      minor = int(match.group(1))
738 abdf0113 Iustin Pop
      state = match.group(2)
739 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
740 abdf0113 Iustin Pop
        continue
741 abdf0113 Iustin Pop
      used_devs[minor] = state, line
742 a8083063 Iustin Pop
743 abdf0113 Iustin Pop
    return used_devs
744 a8083063 Iustin Pop
745 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
746 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
747 0834c866 Iustin Pop

748 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
749 a8083063 Iustin Pop

750 a8083063 Iustin Pop
    """
751 abdf0113 Iustin Pop
    if minor is None:
752 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
753 cb999543 Iustin Pop
      self.attached = False
754 a8083063 Iustin Pop
    else:
755 abdf0113 Iustin Pop
      self.minor = minor
756 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
757 cb999543 Iustin Pop
      self.attached = True
758 a8083063 Iustin Pop
759 a8083063 Iustin Pop
  @staticmethod
760 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
761 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
762 a8083063 Iustin Pop

763 abdf0113 Iustin Pop
    This currently only check the size, which must be around
764 abdf0113 Iustin Pop
    128MiB.
765 a8083063 Iustin Pop

766 a8083063 Iustin Pop
    """
767 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
768 abdf0113 Iustin Pop
    if result.failed:
769 9c793cfb Iustin Pop
      _ThrowError("Failed to get device size: %s - %s",
770 9c793cfb Iustin Pop
                  result.fail_reason, result.output)
771 a8083063 Iustin Pop
    try:
772 abdf0113 Iustin Pop
      sectors = int(result.stdout)
773 abdf0113 Iustin Pop
    except ValueError:
774 9c793cfb Iustin Pop
      _ThrowError("Invalid output from blockdev: '%s'", result.stdout)
775 abdf0113 Iustin Pop
    bytes = sectors * 512
776 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
777 9c793cfb Iustin Pop
      _ThrowError("Meta device too small (%.2fMib)", (bytes / 1024 / 1024))
778 abdf0113 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
779 9c793cfb Iustin Pop
      _ThrowError("Meta device too big (%.2fMiB)", (bytes / 1024 / 1024))
780 a8083063 Iustin Pop
781 abdf0113 Iustin Pop
  def Rename(self, new_id):
782 abdf0113 Iustin Pop
    """Rename a device.
783 a8083063 Iustin Pop

784 abdf0113 Iustin Pop
    This is not supported for drbd devices.
785 a8083063 Iustin Pop

786 a8083063 Iustin Pop
    """
787 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
788 a8083063 Iustin Pop
789 f3e513ad Iustin Pop
790 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
791 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
792 a2cfdea2 Iustin Pop

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

797 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
798 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
799 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
800 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
801 a2cfdea2 Iustin Pop

802 a2cfdea2 Iustin Pop
  """
803 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
804 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
805 a2cfdea2 Iustin Pop
806 cf8df3f3 Iustin Pop
  # timeout constants
807 cf8df3f3 Iustin Pop
  _NET_RECONFIG_TIMEOUT = 60
808 cf8df3f3 Iustin Pop
809 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
810 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
811 fc1dc9d7 Iustin Pop
      children = []
812 464f8daf Iustin Pop
    super(DRBD8, self).__init__(unique_id, children, size)
813 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
814 c3f9340c Guido Trotter
    version = self._GetVersion()
815 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
816 82463074 Iustin Pop
      _ThrowError("Mismatch in DRBD kernel version and requested ganeti"
817 82463074 Iustin Pop
                  " usage: kernel is %s.%s, ganeti wants 8.x",
818 82463074 Iustin Pop
                  version['k_major'], version['k_minor'])
819 a2cfdea2 Iustin Pop
820 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
821 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
822 f9518d38 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
823 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
824 ffa1c0dc Iustin Pop
    (self._lhost, self._lport,
825 ffa1c0dc Iustin Pop
     self._rhost, self._rport,
826 f9518d38 Iustin Pop
     self._aminor, self._secret) = unique_id
827 ffa1c0dc Iustin Pop
    if (self._lhost is not None and self._lhost == self._rhost and
828 ffa1c0dc Iustin Pop
        self._lport == self._rport):
829 ffa1c0dc Iustin Pop
      raise ValueError("Invalid configuration data, same local/remote %s" %
830 ffa1c0dc Iustin Pop
                       (unique_id,))
831 a2cfdea2 Iustin Pop
    self.Attach()
832 a2cfdea2 Iustin Pop
833 a2cfdea2 Iustin Pop
  @classmethod
834 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
835 a2cfdea2 Iustin Pop
    """Initialize a meta device.
836 a2cfdea2 Iustin Pop

837 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
838 a2cfdea2 Iustin Pop

839 a2cfdea2 Iustin Pop
    """
840 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
841 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
842 a2cfdea2 Iustin Pop
    if result.failed:
843 82463074 Iustin Pop
      _ThrowError("Can't initialize meta device: %s", result.output)
844 a2cfdea2 Iustin Pop
845 a2cfdea2 Iustin Pop
  @classmethod
846 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
847 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
848 a2cfdea2 Iustin Pop

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

852 a2cfdea2 Iustin Pop
    """
853 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
854 a2cfdea2 Iustin Pop
855 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
856 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
857 a2cfdea2 Iustin Pop
    highest = None
858 a2cfdea2 Iustin Pop
    for line in data:
859 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
860 a2cfdea2 Iustin Pop
      if match:
861 a2cfdea2 Iustin Pop
        return int(match.group(1))
862 a2cfdea2 Iustin Pop
      match = used_line.match(line)
863 a2cfdea2 Iustin Pop
      if match:
864 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
865 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
866 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
867 a2cfdea2 Iustin Pop
      return 0
868 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
869 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
870 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
871 a2cfdea2 Iustin Pop
    return highest + 1
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 34e71fea Karsten Keil
    addr_type = (pyp.Optional(pyp.Literal("ipv4")).suppress() +
901 34e71fea Karsten Keil
                 pyp.Optional(pyp.Literal("ipv6")).suppress())
902 34e71fea Karsten Keil
    addr_port = (addr_type + pyp.Word(pyp.nums + '.') +
903 34e71fea Karsten Keil
                 pyp.Literal(':').suppress() + number)
904 a2cfdea2 Iustin Pop
    # meta device, extended syntax
905 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
906 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
907 01e2ce3a Iustin Pop
    # device name, extended syntax
908 01e2ce3a Iustin Pop
    device_value = pyp.Literal("minor").suppress() + number
909 a2cfdea2 Iustin Pop
910 a2cfdea2 Iustin Pop
    # a statement
911 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
912 01e2ce3a Iustin Pop
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value ^
913 01e2ce3a Iustin Pop
                         device_value) +
914 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
915 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
916 a2cfdea2 Iustin Pop
917 a2cfdea2 Iustin Pop
    # an entire section
918 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
919 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
920 a2cfdea2 Iustin Pop
921 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
922 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
923 a2cfdea2 Iustin Pop
924 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
925 a2cfdea2 Iustin Pop
926 a2cfdea2 Iustin Pop
    return bnf
927 a2cfdea2 Iustin Pop
928 a2cfdea2 Iustin Pop
  @classmethod
929 3840729d Iustin Pop
  def _GetShowData(cls, minor):
930 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
931 a2cfdea2 Iustin Pop

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

944 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
945 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
946 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
947 3840729d Iustin Pop
    by _GetShowData.
948 3840729d Iustin Pop

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

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

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

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

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

1038 a2cfdea2 Iustin Pop
    """
1039 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
1040 f069addf Iustin Pop
            backend, meta, "0",
1041 f069addf Iustin Pop
            "-d", "%sm" % size,
1042 f069addf Iustin Pop
            "-e", "detach",
1043 f069addf Iustin Pop
            "--create-device"]
1044 333411a7 Guido Trotter
    result = utils.RunCmd(args)
1045 a2cfdea2 Iustin Pop
    if result.failed:
1046 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't attach local disk: %s", minor, result.output)
1047 a2cfdea2 Iustin Pop
1048 a2cfdea2 Iustin Pop
  @classmethod
1049 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
1050 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
1051 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
1052 a2cfdea2 Iustin Pop

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

1103 b00b95dd Iustin Pop
    """
1104 b00b95dd Iustin Pop
    if self.minor is None:
1105 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
1106 82463074 Iustin Pop
                  self._aminor)
1107 b00b95dd Iustin Pop
    if len(devices) != 2:
1108 82463074 Iustin Pop
      _ThrowError("drbd%d: need two devices for AddChildren", self.minor)
1109 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1110 03ece5f3 Iustin Pop
    if "local_dev" in info:
1111 82463074 Iustin Pop
      _ThrowError("drbd%d: already attached to a local disk", self.minor)
1112 b00b95dd Iustin Pop
    backend, meta = devices
1113 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1114 82463074 Iustin Pop
      _ThrowError("drbd%d: children not ready during AddChildren", self.minor)
1115 b00b95dd Iustin Pop
    backend.Open()
1116 b00b95dd Iustin Pop
    meta.Open()
1117 9c793cfb Iustin Pop
    self._CheckMetaSize(meta.dev_path)
1118 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1119 b00b95dd Iustin Pop
1120 f069addf Iustin Pop
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
1121 b00b95dd Iustin Pop
    self._children = devices
1122 b00b95dd Iustin Pop
1123 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1124 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1125 b00b95dd Iustin Pop

1126 b00b95dd Iustin Pop
    """
1127 b00b95dd Iustin Pop
    if self.minor is None:
1128 82463074 Iustin Pop
      _ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
1129 82463074 Iustin Pop
                  self._aminor)
1130 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1131 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1132 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1133 03ece5f3 Iustin Pop
      return
1134 b00b95dd Iustin Pop
    if len(self._children) != 2:
1135 82463074 Iustin Pop
      _ThrowError("drbd%d: we don't have two children: %s", self.minor,
1136 82463074 Iustin Pop
                  self._children)
1137 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1138 82463074 Iustin Pop
      logging.warning("drbd%d: requested detach while detached", self.minor)
1139 e739bd57 Iustin Pop
      return
1140 b00b95dd Iustin Pop
    if len(devices) != 2:
1141 82463074 Iustin Pop
      _ThrowError("drbd%d: we need two children in RemoveChildren", self.minor)
1142 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1143 e739bd57 Iustin Pop
      if dev != child.dev_path:
1144 82463074 Iustin Pop
        _ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
1145 82463074 Iustin Pop
                    " RemoveChildren", self.minor, dev, child.dev_path)
1146 b00b95dd Iustin Pop
1147 1063abd1 Iustin Pop
    self._ShutdownLocal(self.minor)
1148 b00b95dd Iustin Pop
    self._children = []
1149 b00b95dd Iustin Pop
1150 7d585316 Iustin Pop
  @classmethod
1151 7d585316 Iustin Pop
  def _SetMinorSyncSpeed(cls, minor, kbytes):
1152 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1153 a2cfdea2 Iustin Pop

1154 7d585316 Iustin Pop
    This is the low-level implementation.
1155 7d585316 Iustin Pop

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

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

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

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

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

1200 a2cfdea2 Iustin Pop

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

1205 0834c866 Iustin Pop

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

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

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

1215 a2cfdea2 Iustin Pop
    """
1216 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1217 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
1218 6b90c22e Iustin Pop
    stats = self.GetProcStatus()
1219 6b90c22e Iustin Pop
    ldisk = not stats.is_disk_uptodate
1220 6b90c22e Iustin Pop
    is_degraded = not stats.is_connected
1221 6b90c22e Iustin Pop
    return stats.sync_percent, stats.est_time, is_degraded or ldisk, ldisk
1222 a2cfdea2 Iustin Pop
1223 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1224 a2cfdea2 Iustin Pop
    """Make the local state primary.
1225 a2cfdea2 Iustin Pop

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

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

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

1248 a2cfdea2 Iustin Pop
    """
1249 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1250 82463074 Iustin Pop
      _ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
1251 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1252 a2cfdea2 Iustin Pop
    if result.failed:
1253 82463074 Iustin Pop
      _ThrowError("drbd%d: can't switch drbd device to secondary: %s",
1254 82463074 Iustin Pop
                  self.minor, result.output)
1255 a2cfdea2 Iustin Pop
1256 cf8df3f3 Iustin Pop
  def DisconnectNet(self):
1257 cf8df3f3 Iustin Pop
    """Removes network configuration.
1258 cf8df3f3 Iustin Pop

1259 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1260 cf8df3f3 Iustin Pop

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

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

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

1312 cf8df3f3 Iustin Pop
    Args:
1313 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1314 cf8df3f3 Iustin Pop

1315 cf8df3f3 Iustin Pop
    """
1316 cf8df3f3 Iustin Pop
    if self.minor is None:
1317 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1318 cf8df3f3 Iustin Pop
1319 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1320 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1321 cf8df3f3 Iustin Pop
1322 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1323 cf8df3f3 Iustin Pop
1324 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1325 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1326 cf8df3f3 Iustin Pop
1327 1063abd1 Iustin Pop
    self._AssembleNet(self.minor,
1328 1063abd1 Iustin Pop
                      (self._lhost, self._lport, self._rhost, self._rport),
1329 1063abd1 Iustin Pop
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
1330 1063abd1 Iustin Pop
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1331 cf8df3f3 Iustin Pop
1332 a2cfdea2 Iustin Pop
  def Attach(self):
1333 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1334 2d0c8319 Iustin Pop

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

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

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

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

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

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

1379 a2cfdea2 Iustin Pop
    """
1380 1063abd1 Iustin Pop
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
1381 a1578d63 Iustin Pop
    for minor in (self._aminor,):
1382 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1383 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1384 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1385 1063abd1 Iustin Pop
1386 a2cfdea2 Iustin Pop
      if match_l and match_r:
1387 1063abd1 Iustin Pop
        # everything matches
1388 a2cfdea2 Iustin Pop
        break
1389 1063abd1 Iustin Pop
1390 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1391 1063abd1 Iustin Pop
        # disk matches, but not attached to network, attach and recheck
1392 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1393 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1394 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1395 1063abd1 Iustin Pop
          break
1396 1063abd1 Iustin Pop
        else:
1397 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1398 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1399 1063abd1 Iustin Pop
1400 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1401 1063abd1 Iustin Pop
        # no local disk, but network attached and it matches
1402 1063abd1 Iustin Pop
        self._AssembleLocal(minor, self._children[0].dev_path,
1403 f069addf Iustin Pop
                            self._children[1].dev_path, self.size)
1404 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1405 1063abd1 Iustin Pop
          break
1406 1063abd1 Iustin Pop
        else:
1407 1063abd1 Iustin Pop
          _ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
1408 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1409 bf25af3b Iustin Pop
1410 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1411 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1412 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1413 bf25af3b Iustin Pop
      # configuration
1414 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1415 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1416 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1417 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1418 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1419 9cdbe77f Iustin Pop
        # reconnect to our correct one
1420 1063abd1 Iustin Pop
        try:
1421 1063abd1 Iustin Pop
          self._ShutdownNet(minor)
1422 1063abd1 Iustin Pop
        except errors.BlockDeviceError, err:
1423 33bc6f01 Iustin Pop
          _ThrowError("drbd%d: device has correct local storage, wrong"
1424 33bc6f01 Iustin Pop
                      " remote peer and is unable to disconnect in order"
1425 33bc6f01 Iustin Pop
                      " to attach to the correct peer: %s", minor, str(err))
1426 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1427 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1428 9cdbe77f Iustin Pop
        # None)
1429 1063abd1 Iustin Pop
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
1430 1063abd1 Iustin Pop
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1431 1063abd1 Iustin Pop
        if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1432 9cdbe77f Iustin Pop
          break
1433 1063abd1 Iustin Pop
        else:
1434 1063abd1 Iustin Pop
          _ThrowError("drbd%d: network attach successful, but 'drbdsetup"
1435 1063abd1 Iustin Pop
                      " show' disagrees", minor)
1436 9cdbe77f Iustin Pop
1437 a2cfdea2 Iustin Pop
    else:
1438 a2cfdea2 Iustin Pop
      minor = None
1439 a2cfdea2 Iustin Pop
1440 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1441 1063abd1 Iustin Pop
    if minor is None:
1442 1063abd1 Iustin Pop
      _ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
1443 1063abd1 Iustin Pop
                  self._aminor)
1444 a2cfdea2 Iustin Pop
1445 2d0c8319 Iustin Pop
  def _FastAssemble(self):
1446 2d0c8319 Iustin Pop
    """Assemble the drbd device from zero.
1447 a2cfdea2 Iustin Pop

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

1450 a2cfdea2 Iustin Pop
    """
1451 a1578d63 Iustin Pop
    minor = self._aminor
1452 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1453 1063abd1 Iustin Pop
      self._AssembleLocal(minor, self._children[0].dev_path,
1454 f069addf Iustin Pop
                          self._children[1].dev_path, self.size)
1455 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1456 1063abd1 Iustin Pop
      self._AssembleNet(minor,
1457 1063abd1 Iustin Pop
                        (self._lhost, self._lport, self._rhost, self._rport),
1458 1063abd1 Iustin Pop
                        constants.DRBD_NET_PROTOCOL,
1459 1063abd1 Iustin Pop
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
1460 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1461 a2cfdea2 Iustin Pop
1462 a2cfdea2 Iustin Pop
  @classmethod
1463 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1464 b00b95dd Iustin Pop
    """Detach from the local device.
1465 b00b95dd Iustin Pop

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

1469 b00b95dd Iustin Pop
    """
1470 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1471 b00b95dd Iustin Pop
    if result.failed:
1472 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't detach local disk: %s", minor, result.output)
1473 b00b95dd Iustin Pop
1474 b00b95dd Iustin Pop
  @classmethod
1475 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1476 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1477 f3e513ad Iustin Pop

1478 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1479 f3e513ad Iustin Pop

1480 f3e513ad Iustin Pop
    """
1481 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1482 a8459f1c Iustin Pop
    if result.failed:
1483 1063abd1 Iustin Pop
      _ThrowError("drbd%d: can't shutdown network: %s", minor, result.output)
1484 f3e513ad Iustin Pop
1485 f3e513ad Iustin Pop
  @classmethod
1486 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1487 a2cfdea2 Iustin Pop
    """Deactivate the device.
1488 a2cfdea2 Iustin Pop

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

1491 a2cfdea2 Iustin Pop
    """
1492 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1493 a2cfdea2 Iustin Pop
    if result.failed:
1494 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't shutdown drbd device: %s",
1495 33bc6f01 Iustin Pop
                  minor, result.output)
1496 a2cfdea2 Iustin Pop
1497 a2cfdea2 Iustin Pop
  def Shutdown(self):
1498 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1499 a2cfdea2 Iustin Pop

1500 a2cfdea2 Iustin Pop
    """
1501 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1502 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1503 746f7476 Iustin Pop
      return
1504 746f7476 Iustin Pop
    minor = self.minor
1505 a2cfdea2 Iustin Pop
    self.minor = None
1506 a2cfdea2 Iustin Pop
    self.dev_path = None
1507 746f7476 Iustin Pop
    self._ShutdownAll(minor)
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 0c6c04ec Iustin Pop
    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 767d52d3 Iustin Pop
    # check that the minor is unused
1526 767d52d3 Iustin Pop
    aminor = unique_id[4]
1527 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1528 767d52d3 Iustin Pop
    if aminor in proc_info:
1529 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1530 767d52d3 Iustin Pop
      in_use = status.is_in_use
1531 767d52d3 Iustin Pop
    else:
1532 767d52d3 Iustin Pop
      in_use = False
1533 767d52d3 Iustin Pop
    if in_use:
1534 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: minor is already in use at Create() time", aminor)
1535 a2cfdea2 Iustin Pop
    meta = children[1]
1536 a2cfdea2 Iustin Pop
    meta.Assemble()
1537 a2cfdea2 Iustin Pop
    if not meta.Attach():
1538 33bc6f01 Iustin Pop
      _ThrowError("drbd%d: can't attach to meta device '%s'",
1539 33bc6f01 Iustin Pop
                  aminor, meta)
1540 9c793cfb Iustin Pop
    cls._CheckMetaSize(meta.dev_path)
1541 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1542 464f8daf Iustin Pop
    return cls(unique_id, children, size)
1543 a2cfdea2 Iustin Pop
1544 1005d816 Iustin Pop
  def Grow(self, amount):
1545 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1546 1005d816 Iustin Pop

1547 1005d816 Iustin Pop
    """
1548 1005d816 Iustin Pop
    if self.minor is None:
1549 82463074 Iustin Pop
      _ThrowError("drbd%d: Grow called while not attached", self._aminor)
1550 1005d816 Iustin Pop
    if len(self._children) != 2 or None in self._children:
1551 82463074 Iustin Pop
      _ThrowError("drbd%d: cannot grow diskless device", self.minor)
1552 1005d816 Iustin Pop
    self._children[0].Grow(amount)
1553 1005d816 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "resize"])
1554 1005d816 Iustin Pop
    if result.failed:
1555 82463074 Iustin Pop
      _ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
1556 1005d816 Iustin Pop
1557 a8083063 Iustin Pop
1558 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1559 6f695a2e Manuel Franceschini
  """File device.
1560 abdf0113 Iustin Pop

1561 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1562 6f695a2e Manuel Franceschini

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

1565 6f695a2e Manuel Franceschini
  """
1566 464f8daf Iustin Pop
  def __init__(self, unique_id, children, size):
1567 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1568 6f695a2e Manuel Franceschini

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

1582 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1583 6f695a2e Manuel Franceschini

1584 6f695a2e Manuel Franceschini
    """
1585 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1586 1063abd1 Iustin Pop
      _ThrowError("File device '%s' does not exist" % self.dev_path)
1587 6f695a2e Manuel Franceschini
1588 6f695a2e Manuel Franceschini
  def Shutdown(self):
1589 6f695a2e Manuel Franceschini
    """Shutdown the device.
1590 6f695a2e Manuel Franceschini

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

1594 6f695a2e Manuel Franceschini
    """
1595 746f7476 Iustin Pop
    pass
1596 6f695a2e Manuel Franceschini
1597 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1598 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1599 6f695a2e Manuel Franceschini

1600 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1601 6f695a2e Manuel Franceschini

1602 6f695a2e Manuel Franceschini
    """
1603 6f695a2e Manuel Franceschini
    pass
1604 6f695a2e Manuel Franceschini
1605 6f695a2e Manuel Franceschini
  def Close(self):
1606 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1607 6f695a2e Manuel Franceschini

1608 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1609 6f695a2e Manuel Franceschini

1610 6f695a2e Manuel Franceschini
    """
1611 6f695a2e Manuel Franceschini
    pass
1612 6f695a2e Manuel Franceschini
1613 6f695a2e Manuel Franceschini
  def Remove(self):
1614 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1615 6f695a2e Manuel Franceschini

1616 c41eea6e Iustin Pop
    @rtype: boolean
1617 c41eea6e Iustin Pop
    @return: True if the removal was successful
1618 6f695a2e Manuel Franceschini

1619 6f695a2e Manuel Franceschini
    """
1620 6f695a2e Manuel Franceschini
    try:
1621 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1622 6f695a2e Manuel Franceschini
    except OSError, err:
1623 0c6c04ec Iustin Pop
      if err.errno != errno.ENOENT:
1624 0c6c04ec Iustin Pop
        _ThrowError("Can't remove file '%s': %s", self.dev_path, err)
1625 6f695a2e Manuel Franceschini
1626 6f695a2e Manuel Franceschini
  def Attach(self):
1627 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1628 6f695a2e Manuel Franceschini

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

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

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

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

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

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

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

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

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

1691 a8083063 Iustin Pop
  """
1692 a8083063 Iustin Pop
  if dev_type not in DEV_MAP:
1693 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid block device type '%s'" % dev_type)
1694 464f8daf Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children, size)
1695 1063abd1 Iustin Pop
  device.Assemble()
1696 a8083063 Iustin Pop
  return device
1697 a8083063 Iustin Pop
1698 a8083063 Iustin Pop
1699 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1700 a8083063 Iustin Pop
  """Create a device.
1701 a8083063 Iustin Pop

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