Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ 0c6c04ec

History | View | Annotate | Download (53.4 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 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
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 a8083063 Iustin Pop
119 a8083063 Iustin Pop
  def Assemble(self):
120 a8083063 Iustin Pop
    """Assemble the device from its components.
121 a8083063 Iustin Pop

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

130 a8083063 Iustin Pop
    """
131 f87548b5 Iustin Pop
    return True
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
  def Attach(self):
134 a8083063 Iustin Pop
    """Find a device which matches our config and attach to it.
135 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

441 a8083063 Iustin Pop
    """
442 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
443 5574047a Iustin Pop
    if result.failed:
444 468c5f77 Iustin Pop
      logging.error("Can't activate lv %s: %s", self.dev_path, result.output)
445 cb999543 Iustin Pop
      return False
446 cb999543 Iustin Pop
    return self.Attach()
447 a8083063 Iustin Pop
448 a8083063 Iustin Pop
  def Shutdown(self):
449 a8083063 Iustin Pop
    """Shutdown the device.
450 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

643 a8083063 Iustin Pop
    """
644 6b90c22e Iustin Pop
    stat = open(filename, "r")
645 abdf0113 Iustin Pop
    try:
646 abdf0113 Iustin Pop
      data = stat.read().splitlines()
647 abdf0113 Iustin Pop
    finally:
648 abdf0113 Iustin Pop
      stat.close()
649 abdf0113 Iustin Pop
    if not data:
650 82463074 Iustin Pop
      _ThrowError("Can't read any data from %s", filename)
651 abdf0113 Iustin Pop
    return data
652 a8083063 Iustin Pop
653 abdf0113 Iustin Pop
  @staticmethod
654 abdf0113 Iustin Pop
  def _MassageProcData(data):
655 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
656 a8083063 Iustin Pop

657 c41eea6e Iustin Pop
    @return: a dictionary of minor: joined lines from /proc/drbd
658 c41eea6e Iustin Pop
        for that minor
659 a8083063 Iustin Pop

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

683 abdf0113 Iustin Pop
    This will return a dict with keys:
684 c41eea6e Iustin Pop
      - k_major
685 c41eea6e Iustin Pop
      - k_minor
686 c41eea6e Iustin Pop
      - k_point
687 c41eea6e Iustin Pop
      - api
688 c41eea6e Iustin Pop
      - proto
689 c41eea6e Iustin Pop
      - proto2 (only on drbd > 8.2.X)
690 a8083063 Iustin Pop

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

715 a8083063 Iustin Pop
    """
716 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
717 a8083063 Iustin Pop
718 abdf0113 Iustin Pop
  @classmethod
719 6d2e83d5 Iustin Pop
  def GetUsedDevs(cls):
720 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
721 a8083063 Iustin Pop

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

742 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
743 a8083063 Iustin Pop

744 a8083063 Iustin Pop
    """
745 abdf0113 Iustin Pop
    if minor is None:
746 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
747 cb999543 Iustin Pop
      self.attached = False
748 a8083063 Iustin Pop
    else:
749 abdf0113 Iustin Pop
      self.minor = minor
750 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
751 cb999543 Iustin Pop
      self.attached = True
752 a8083063 Iustin Pop
753 a8083063 Iustin Pop
  @staticmethod
754 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
755 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
756 a8083063 Iustin Pop

757 abdf0113 Iustin Pop
    This currently only check the size, which must be around
758 abdf0113 Iustin Pop
    128MiB.
759 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

851 a2cfdea2 Iustin Pop
    """
852 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
853 a2cfdea2 Iustin Pop
854 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
855 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
856 a2cfdea2 Iustin Pop
    highest = None
857 a2cfdea2 Iustin Pop
    for line in data:
858 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
859 a2cfdea2 Iustin Pop
      if match:
860 a2cfdea2 Iustin Pop
        return int(match.group(1))
861 a2cfdea2 Iustin Pop
      match = used_line.match(line)
862 a2cfdea2 Iustin Pop
      if match:
863 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
864 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
865 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
866 a2cfdea2 Iustin Pop
      return 0
867 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
868 468c5f77 Iustin Pop
      logging.error("Error: no free drbd minors!")
869 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
870 a2cfdea2 Iustin Pop
    return highest + 1
871 a2cfdea2 Iustin Pop
872 a2cfdea2 Iustin Pop
  @classmethod
873 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
874 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
875 a2cfdea2 Iustin Pop

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1197 a2cfdea2 Iustin Pop

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

1202 0834c866 Iustin Pop

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

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

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

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

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

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

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

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

1256 cf8df3f3 Iustin Pop
    This method shutdowns the network side of the device.
1257 cf8df3f3 Iustin Pop

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

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

1304 cf8df3f3 Iustin Pop
    This method connects the network side of the device with a
1305 cf8df3f3 Iustin Pop
    specified multi-master flag. The device needs to be 'Standalone'
1306 cf8df3f3 Iustin Pop
    but have valid network configuration data.
1307 cf8df3f3 Iustin Pop

1308 cf8df3f3 Iustin Pop
    Args:
1309 cf8df3f3 Iustin Pop
      - multimaster: init the network in dual-primary mode
1310 cf8df3f3 Iustin Pop

1311 cf8df3f3 Iustin Pop
    """
1312 cf8df3f3 Iustin Pop
    if self.minor is None:
1313 82463074 Iustin Pop
      _ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
1314 cf8df3f3 Iustin Pop
1315 cf8df3f3 Iustin Pop
    if None in (self._lhost, self._lport, self._rhost, self._rport):
1316 82463074 Iustin Pop
      _ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
1317 cf8df3f3 Iustin Pop
1318 cf8df3f3 Iustin Pop
    status = self.GetProcStatus()
1319 cf8df3f3 Iustin Pop
1320 cf8df3f3 Iustin Pop
    if not status.is_standalone:
1321 82463074 Iustin Pop
      _ThrowError("drbd%d: device is not standalone in AttachNet", self.minor)
1322 cf8df3f3 Iustin Pop
1323 cf8df3f3 Iustin Pop
    return self._AssembleNet(self.minor,
1324 cf8df3f3 Iustin Pop
                             (self._lhost, self._lport,
1325 cf8df3f3 Iustin Pop
                              self._rhost, self._rport),
1326 cf8df3f3 Iustin Pop
                             "C", dual_pri=multimaster)
1327 cf8df3f3 Iustin Pop
1328 a2cfdea2 Iustin Pop
  def Attach(self):
1329 2d0c8319 Iustin Pop
    """Check if our minor is configured.
1330 2d0c8319 Iustin Pop

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

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

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

1351 2d0c8319 Iustin Pop
    Method:
1352 2d0c8319 Iustin Pop
      - if we have a configured device, we try to ensure that it matches
1353 2d0c8319 Iustin Pop
        our config
1354 2d0c8319 Iustin Pop
      - if not, we create it from zero
1355 2d0c8319 Iustin Pop

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

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

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

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

1437 a2cfdea2 Iustin Pop
    """
1438 a1578d63 Iustin Pop
    # TODO: maybe completely tear-down the minor (drbdsetup ... down)
1439 a1578d63 Iustin Pop
    # before attaching our own?
1440 a1578d63 Iustin Pop
    minor = self._aminor
1441 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1442 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1443 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1444 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1445 a2cfdea2 Iustin Pop
      if not result:
1446 a2cfdea2 Iustin Pop
        return False
1447 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1448 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1449 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1450 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1451 3c03759a Iustin Pop
                                 constants.DRBD_NET_PROTOCOL,
1452 3c03759a Iustin Pop
                                 hmac=constants.DRBD_HMAC_ALG,
1453 2899d9de Iustin Pop
                                 secret=self._secret)
1454 a2cfdea2 Iustin Pop
      if not result:
1455 a2cfdea2 Iustin Pop
        return False
1456 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1457 a2cfdea2 Iustin Pop
    return True
1458 a2cfdea2 Iustin Pop
1459 a2cfdea2 Iustin Pop
  @classmethod
1460 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1461 b00b95dd Iustin Pop
    """Detach from the local device.
1462 b00b95dd Iustin Pop

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

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

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

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

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

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

1498 a2cfdea2 Iustin Pop
    """
1499 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1500 746f7476 Iustin Pop
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
1501 746f7476 Iustin Pop
      return
1502 746f7476 Iustin Pop
    minor = self.minor
1503 a2cfdea2 Iustin Pop
    self.minor = None
1504 a2cfdea2 Iustin Pop
    self.dev_path = None
1505 746f7476 Iustin Pop
    self._ShutdownAll(minor)
1506 a2cfdea2 Iustin Pop
1507 a2cfdea2 Iustin Pop
  def Remove(self):
1508 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1509 a2cfdea2 Iustin Pop

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

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

1520 a2cfdea2 Iustin Pop
    """
1521 a2cfdea2 Iustin Pop
    if len(children) != 2:
1522 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1523 767d52d3 Iustin Pop
    # check that the minor is unused
1524 767d52d3 Iustin Pop
    aminor = unique_id[4]
1525 767d52d3 Iustin Pop
    proc_info = cls._MassageProcData(cls._GetProcData())
1526 767d52d3 Iustin Pop
    if aminor in proc_info:
1527 767d52d3 Iustin Pop
      status = DRBD8Status(proc_info[aminor])
1528 767d52d3 Iustin Pop
      in_use = status.is_in_use
1529 767d52d3 Iustin Pop
    else:
1530 767d52d3 Iustin Pop
      in_use = False
1531 767d52d3 Iustin Pop
    if in_use:
1532 82463074 Iustin Pop
      _ThrowError("DRBD minor %d already in use at Create() time", aminor)
1533 a2cfdea2 Iustin Pop
    meta = children[1]
1534 a2cfdea2 Iustin Pop
    meta.Assemble()
1535 a2cfdea2 Iustin Pop
    if not meta.Attach():
1536 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1537 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1538 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1539 3b559640 Iustin Pop
    cls._InitMeta(aminor, meta.dev_path)
1540 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1541 a2cfdea2 Iustin Pop
1542 1005d816 Iustin Pop
  def Grow(self, amount):
1543 1005d816 Iustin Pop
    """Resize the DRBD device and its backing storage.
1544 1005d816 Iustin Pop

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

1559 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1560 6f695a2e Manuel Franceschini

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

1563 6f695a2e Manuel Franceschini
  """
1564 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1565 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1566 6f695a2e Manuel Franceschini

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

1580 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1581 6f695a2e Manuel Franceschini

1582 6f695a2e Manuel Franceschini
    """
1583 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1584 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1585 6f695a2e Manuel Franceschini
                                    self.dev_path)
1586 6f695a2e Manuel Franceschini
    return True
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 6c626518 Iustin Pop
    # TODO: decide whether we should check for existing files and
1649 6c626518 Iustin Pop
    # abort or not
1650 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1651 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1652 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
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 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
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 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
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 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
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 f96e3c4f Iustin Pop
def Assemble(dev_type, unique_id, children):
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 a8083063 Iustin Pop
  device = DEV_MAP[dev_type](unique_id, children)
1695 f96e3c4f Iustin Pop
  if not device.Assemble():
1696 f96e3c4f Iustin Pop
    raise errors.BlockDeviceError("Can't find a valid block device for"
1697 f96e3c4f Iustin Pop
                                  " %s/%s/%s" %
1698 f96e3c4f Iustin Pop
                                  (dev_type, unique_id, children))
1699 a8083063 Iustin Pop
  return device
1700 a8083063 Iustin Pop
1701 a8083063 Iustin Pop
1702 a8083063 Iustin Pop
def Create(dev_type, unique_id, children, size):
1703 a8083063 Iustin Pop
  """Create a device.
1704 a8083063 Iustin Pop

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