Statistics
| Branch: | Tag: | Revision:

root / lib / bdev.py @ c9673d92

History | View | Annotate | Download (46.5 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

204 a8083063 Iustin Pop
    Returns:
205 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
206 0834c866 Iustin Pop

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

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

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

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

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

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

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

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

254 a0c3fea1 Michael Hanselmann
    """
255 a0c3fea1 Michael Hanselmann
    for child in self._children:
256 a0c3fea1 Michael Hanselmann
      child.SetInfo(text)
257 a0c3fea1 Michael Hanselmann
258 a0c3fea1 Michael Hanselmann
259 a8083063 Iustin Pop
  def __repr__(self):
260 a8083063 Iustin Pop
    return ("<%s: unique_id: %s, children: %s, %s:%s, %s>" %
261 a8083063 Iustin Pop
            (self.__class__, self.unique_id, self._children,
262 a8083063 Iustin Pop
             self.major, self.minor, self.dev_path))
263 a8083063 Iustin Pop
264 a8083063 Iustin Pop
265 a8083063 Iustin Pop
class LogicalVolume(BlockDev):
266 a8083063 Iustin Pop
  """Logical Volume block device.
267 a8083063 Iustin Pop

268 a8083063 Iustin Pop
  """
269 a8083063 Iustin Pop
  def __init__(self, unique_id, children):
270 a8083063 Iustin Pop
    """Attaches to a LV device.
271 a8083063 Iustin Pop

272 a8083063 Iustin Pop
    The unique_id is a tuple (vg_name, lv_name)
273 a8083063 Iustin Pop

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

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

316 a8083063 Iustin Pop
    Args:
317 a8083063 Iustin Pop
      vg_name: the volume group name
318 a8083063 Iustin Pop

319 a8083063 Iustin Pop
    Returns:
320 a8083063 Iustin Pop
      list of (free_space, name) with free_space in mebibytes
321 098c0958 Michael Hanselmann

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

347 a8083063 Iustin Pop
    """
348 a8083063 Iustin Pop
    if not self.minor and not self.Attach():
349 a8083063 Iustin Pop
      # the LV does not exist
350 a8083063 Iustin Pop
      return True
351 a8083063 Iustin Pop
    result = utils.RunCmd(["lvremove", "-f", "%s/%s" %
352 a8083063 Iustin Pop
                           (self._vg_name, self._lv_name)])
353 a8083063 Iustin Pop
    if result.failed:
354 6c896e2f Iustin Pop
      logger.Error("Can't lvremove: %s - %s" %
355 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
356 a8083063 Iustin Pop
357 a8083063 Iustin Pop
    return not result.failed
358 a8083063 Iustin Pop
359 f3e513ad Iustin Pop
  def Rename(self, new_id):
360 f3e513ad Iustin Pop
    """Rename this logical volume.
361 f3e513ad Iustin Pop

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

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

384 a8083063 Iustin Pop
    """
385 a8083063 Iustin Pop
    result = utils.RunCmd(["lvdisplay", self.dev_path])
386 a8083063 Iustin Pop
    if result.failed:
387 fcb1f331 Michael Hanselmann
      logger.Error("Can't find LV %s: %s, %s" %
388 fcb1f331 Michael Hanselmann
                   (self.dev_path, result.fail_reason, result.output))
389 a8083063 Iustin Pop
      return False
390 a8083063 Iustin Pop
    match = re.compile("^ *Block device *([0-9]+):([0-9]+).*$")
391 a8083063 Iustin Pop
    for line in result.stdout.splitlines():
392 a8083063 Iustin Pop
      match_result = match.match(line)
393 a8083063 Iustin Pop
      if match_result:
394 a8083063 Iustin Pop
        self.major = int(match_result.group(1))
395 a8083063 Iustin Pop
        self.minor = int(match_result.group(2))
396 a8083063 Iustin Pop
        return True
397 a8083063 Iustin Pop
    return False
398 a8083063 Iustin Pop
399 a8083063 Iustin Pop
  def Assemble(self):
400 a8083063 Iustin Pop
    """Assemble the device.
401 a8083063 Iustin Pop

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

406 a8083063 Iustin Pop
    """
407 5574047a Iustin Pop
    result = utils.RunCmd(["lvchange", "-ay", self.dev_path])
408 5574047a Iustin Pop
    if result.failed:
409 5574047a Iustin Pop
      logger.Error("Can't activate lv %s: %s" % (self.dev_path, result.output))
410 5574047a Iustin Pop
    return not result.failed
411 a8083063 Iustin Pop
412 a8083063 Iustin Pop
  def Shutdown(self):
413 a8083063 Iustin Pop
    """Shutdown the device.
414 a8083063 Iustin Pop

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

418 a8083063 Iustin Pop
    """
419 a8083063 Iustin Pop
    return True
420 a8083063 Iustin Pop
421 9db6dbce Iustin Pop
  def GetSyncStatus(self):
422 9db6dbce Iustin Pop
    """Returns the sync status of the device.
423 9db6dbce Iustin Pop

424 9db6dbce Iustin Pop
    If this device is a mirroring device, this function returns the
425 9db6dbce Iustin Pop
    status of the mirror.
426 9db6dbce Iustin Pop

427 9db6dbce Iustin Pop
    Returns:
428 0834c866 Iustin Pop
     (sync_percent, estimated_time, is_degraded, ldisk)
429 9db6dbce Iustin Pop

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

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

441 9db6dbce Iustin Pop
    """
442 9db6dbce Iustin Pop
    result = utils.RunCmd(["lvs", "--noheadings", "-olv_attr", self.dev_path])
443 9db6dbce Iustin Pop
    if result.failed:
444 6c896e2f Iustin Pop
      logger.Error("Can't display lv: %s - %s" %
445 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
446 0834c866 Iustin Pop
      return None, None, True, True
447 9db6dbce Iustin Pop
    out = result.stdout.strip()
448 9db6dbce Iustin Pop
    # format: type/permissions/alloc/fixed_minor/state/open
449 9db6dbce Iustin Pop
    if len(out) != 6:
450 0834c866 Iustin Pop
      logger.Debug("Error in lvs output: attrs=%s, len != 6" % out)
451 0834c866 Iustin Pop
      return None, None, True, True
452 0834c866 Iustin Pop
    ldisk = out[0] == 'v' # virtual volume, i.e. doesn't have
453 0834c866 Iustin Pop
                          # backing storage
454 0834c866 Iustin Pop
    return None, None, ldisk, ldisk
455 9db6dbce Iustin Pop
456 a8083063 Iustin Pop
  def Open(self, force=False):
457 a8083063 Iustin Pop
    """Make the device ready for I/O.
458 a8083063 Iustin Pop

459 a8083063 Iustin Pop
    This is a no-op for the LV device type.
460 a8083063 Iustin Pop

461 a8083063 Iustin Pop
    """
462 fdbd668d Iustin Pop
    pass
463 a8083063 Iustin Pop
464 a8083063 Iustin Pop
  def Close(self):
465 a8083063 Iustin Pop
    """Notifies that the device will no longer be used for I/O.
466 a8083063 Iustin Pop

467 a8083063 Iustin Pop
    This is a no-op for the LV device type.
468 a8083063 Iustin Pop

469 a8083063 Iustin Pop
    """
470 fdbd668d Iustin Pop
    pass
471 a8083063 Iustin Pop
472 a8083063 Iustin Pop
  def Snapshot(self, size):
473 a8083063 Iustin Pop
    """Create a snapshot copy of an lvm block device.
474 a8083063 Iustin Pop

475 a8083063 Iustin Pop
    """
476 a8083063 Iustin Pop
    snap_name = self._lv_name + ".snap"
477 a8083063 Iustin Pop
478 a8083063 Iustin Pop
    # remove existing snapshot if found
479 a8083063 Iustin Pop
    snap = LogicalVolume((self._vg_name, snap_name), None)
480 a8083063 Iustin Pop
    snap.Remove()
481 a8083063 Iustin Pop
482 a8083063 Iustin Pop
    pvs_info = self.GetPVInfo(self._vg_name)
483 a8083063 Iustin Pop
    if not pvs_info:
484 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Can't compute PV info for vg %s" %
485 3ecf6786 Iustin Pop
                                    self._vg_name)
486 a8083063 Iustin Pop
    pvs_info.sort()
487 a8083063 Iustin Pop
    pvs_info.reverse()
488 a8083063 Iustin Pop
    free_size, pv_name = pvs_info[0]
489 a8083063 Iustin Pop
    if free_size < size:
490 3ecf6786 Iustin Pop
      raise errors.BlockDeviceError("Not enough free space: required %s,"
491 3ecf6786 Iustin Pop
                                    " available %s" % (size, free_size))
492 a8083063 Iustin Pop
493 a8083063 Iustin Pop
    result = utils.RunCmd(["lvcreate", "-L%dm" % size, "-s",
494 a8083063 Iustin Pop
                           "-n%s" % snap_name, self.dev_path])
495 a8083063 Iustin Pop
    if result.failed:
496 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("command: %s error: %s - %s" %
497 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
498 6c896e2f Iustin Pop
                                     result.output))
499 a8083063 Iustin Pop
500 a8083063 Iustin Pop
    return snap_name
501 a8083063 Iustin Pop
502 a0c3fea1 Michael Hanselmann
  def SetInfo(self, text):
503 a0c3fea1 Michael Hanselmann
    """Update metadata with info text.
504 a0c3fea1 Michael Hanselmann

505 a0c3fea1 Michael Hanselmann
    """
506 a0c3fea1 Michael Hanselmann
    BlockDev.SetInfo(self, text)
507 a0c3fea1 Michael Hanselmann
508 a0c3fea1 Michael Hanselmann
    # Replace invalid characters
509 a0c3fea1 Michael Hanselmann
    text = re.sub('^[^A-Za-z0-9_+.]', '_', text)
510 a0c3fea1 Michael Hanselmann
    text = re.sub('[^-A-Za-z0-9_+.]', '_', text)
511 a0c3fea1 Michael Hanselmann
512 a0c3fea1 Michael Hanselmann
    # Only up to 128 characters are allowed
513 a0c3fea1 Michael Hanselmann
    text = text[:128]
514 a0c3fea1 Michael Hanselmann
515 a0c3fea1 Michael Hanselmann
    result = utils.RunCmd(["lvchange", "--addtag", text,
516 a0c3fea1 Michael Hanselmann
                           self.dev_path])
517 a0c3fea1 Michael Hanselmann
    if result.failed:
518 6c896e2f Iustin Pop
      raise errors.BlockDeviceError("Command: %s error: %s - %s" %
519 6c896e2f Iustin Pop
                                    (result.cmd, result.fail_reason,
520 6c896e2f Iustin Pop
                                     result.output))
521 a0c3fea1 Michael Hanselmann
522 a0c3fea1 Michael Hanselmann
523 0f7f32d9 Iustin Pop
class BaseDRBD(BlockDev):
524 0f7f32d9 Iustin Pop
  """Base DRBD class.
525 a8083063 Iustin Pop

526 0f7f32d9 Iustin Pop
  This class contains a few bits of common functionality between the
527 0f7f32d9 Iustin Pop
  0.7 and 8.x versions of DRBD.
528 0f7f32d9 Iustin Pop

529 abdf0113 Iustin Pop
  """
530 abdf0113 Iustin Pop
  _VERSION_RE = re.compile(r"^version: (\d+)\.(\d+)\.(\d+)"
531 abdf0113 Iustin Pop
                           r" \(api:(\d+)/proto:(\d+)(?:-(\d+))?\)")
532 a8083063 Iustin Pop
533 abdf0113 Iustin Pop
  _DRBD_MAJOR = 147
534 abdf0113 Iustin Pop
  _ST_UNCONFIGURED = "Unconfigured"
535 abdf0113 Iustin Pop
  _ST_WFCONNECTION = "WFConnection"
536 abdf0113 Iustin Pop
  _ST_CONNECTED = "Connected"
537 a8083063 Iustin Pop
538 abdf0113 Iustin Pop
  @staticmethod
539 abdf0113 Iustin Pop
  def _GetProcData():
540 abdf0113 Iustin Pop
    """Return data from /proc/drbd.
541 a8083063 Iustin Pop

542 a8083063 Iustin Pop
    """
543 abdf0113 Iustin Pop
    stat = open("/proc/drbd", "r")
544 abdf0113 Iustin Pop
    try:
545 abdf0113 Iustin Pop
      data = stat.read().splitlines()
546 abdf0113 Iustin Pop
    finally:
547 abdf0113 Iustin Pop
      stat.close()
548 abdf0113 Iustin Pop
    if not data:
549 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't read any data from /proc/drbd")
550 abdf0113 Iustin Pop
    return data
551 a8083063 Iustin Pop
552 abdf0113 Iustin Pop
  @staticmethod
553 abdf0113 Iustin Pop
  def _MassageProcData(data):
554 abdf0113 Iustin Pop
    """Transform the output of _GetProdData into a nicer form.
555 a8083063 Iustin Pop

556 abdf0113 Iustin Pop
    Returns:
557 abdf0113 Iustin Pop
      a dictionary of minor: joined lines from /proc/drbd for that minor
558 a8083063 Iustin Pop

559 a8083063 Iustin Pop
    """
560 abdf0113 Iustin Pop
    lmatch = re.compile("^ *([0-9]+):.*$")
561 abdf0113 Iustin Pop
    results = {}
562 abdf0113 Iustin Pop
    old_minor = old_line = None
563 abdf0113 Iustin Pop
    for line in data:
564 abdf0113 Iustin Pop
      lresult = lmatch.match(line)
565 abdf0113 Iustin Pop
      if lresult is not None:
566 abdf0113 Iustin Pop
        if old_minor is not None:
567 abdf0113 Iustin Pop
          results[old_minor] = old_line
568 abdf0113 Iustin Pop
        old_minor = int(lresult.group(1))
569 abdf0113 Iustin Pop
        old_line = line
570 abdf0113 Iustin Pop
      else:
571 abdf0113 Iustin Pop
        if old_minor is not None:
572 abdf0113 Iustin Pop
          old_line += " " + line.strip()
573 abdf0113 Iustin Pop
    # add last line
574 abdf0113 Iustin Pop
    if old_minor is not None:
575 abdf0113 Iustin Pop
      results[old_minor] = old_line
576 abdf0113 Iustin Pop
    return results
577 a8083063 Iustin Pop
578 abdf0113 Iustin Pop
  @classmethod
579 abdf0113 Iustin Pop
  def _GetVersion(cls):
580 abdf0113 Iustin Pop
    """Return the DRBD version.
581 a8083063 Iustin Pop

582 abdf0113 Iustin Pop
    This will return a dict with keys:
583 abdf0113 Iustin Pop
      k_major,
584 abdf0113 Iustin Pop
      k_minor,
585 abdf0113 Iustin Pop
      k_point,
586 abdf0113 Iustin Pop
      api,
587 abdf0113 Iustin Pop
      proto,
588 abdf0113 Iustin Pop
      proto2 (only on drbd > 8.2.X)
589 a8083063 Iustin Pop

590 a8083063 Iustin Pop
    """
591 abdf0113 Iustin Pop
    proc_data = cls._GetProcData()
592 abdf0113 Iustin Pop
    first_line = proc_data[0].strip()
593 abdf0113 Iustin Pop
    version = cls._VERSION_RE.match(first_line)
594 abdf0113 Iustin Pop
    if not version:
595 abdf0113 Iustin Pop
      raise errors.BlockDeviceError("Can't parse DRBD version from '%s'" %
596 abdf0113 Iustin Pop
                                    first_line)
597 a8083063 Iustin Pop
598 abdf0113 Iustin Pop
    values = version.groups()
599 abdf0113 Iustin Pop
    retval = {'k_major': int(values[0]),
600 abdf0113 Iustin Pop
              'k_minor': int(values[1]),
601 abdf0113 Iustin Pop
              'k_point': int(values[2]),
602 abdf0113 Iustin Pop
              'api': int(values[3]),
603 abdf0113 Iustin Pop
              'proto': int(values[4]),
604 abdf0113 Iustin Pop
             }
605 abdf0113 Iustin Pop
    if values[5] is not None:
606 abdf0113 Iustin Pop
      retval['proto2'] = values[5]
607 a8083063 Iustin Pop
608 abdf0113 Iustin Pop
    return retval
609 abdf0113 Iustin Pop
610 abdf0113 Iustin Pop
  @staticmethod
611 abdf0113 Iustin Pop
  def _DevPath(minor):
612 abdf0113 Iustin Pop
    """Return the path to a drbd device for a given minor.
613 a8083063 Iustin Pop

614 a8083063 Iustin Pop
    """
615 abdf0113 Iustin Pop
    return "/dev/drbd%d" % minor
616 a8083063 Iustin Pop
617 abdf0113 Iustin Pop
  @classmethod
618 abdf0113 Iustin Pop
  def _GetUsedDevs(cls):
619 abdf0113 Iustin Pop
    """Compute the list of used DRBD devices.
620 a8083063 Iustin Pop

621 a8083063 Iustin Pop
    """
622 abdf0113 Iustin Pop
    data = cls._GetProcData()
623 a8083063 Iustin Pop
624 abdf0113 Iustin Pop
    used_devs = {}
625 abdf0113 Iustin Pop
    valid_line = re.compile("^ *([0-9]+): cs:([^ ]+).*$")
626 abdf0113 Iustin Pop
    for line in data:
627 abdf0113 Iustin Pop
      match = valid_line.match(line)
628 abdf0113 Iustin Pop
      if not match:
629 abdf0113 Iustin Pop
        continue
630 abdf0113 Iustin Pop
      minor = int(match.group(1))
631 abdf0113 Iustin Pop
      state = match.group(2)
632 abdf0113 Iustin Pop
      if state == cls._ST_UNCONFIGURED:
633 abdf0113 Iustin Pop
        continue
634 abdf0113 Iustin Pop
      used_devs[minor] = state, line
635 a8083063 Iustin Pop
636 abdf0113 Iustin Pop
    return used_devs
637 a8083063 Iustin Pop
638 abdf0113 Iustin Pop
  def _SetFromMinor(self, minor):
639 abdf0113 Iustin Pop
    """Set our parameters based on the given minor.
640 0834c866 Iustin Pop

641 abdf0113 Iustin Pop
    This sets our minor variable and our dev_path.
642 a8083063 Iustin Pop

643 a8083063 Iustin Pop
    """
644 abdf0113 Iustin Pop
    if minor is None:
645 abdf0113 Iustin Pop
      self.minor = self.dev_path = None
646 a8083063 Iustin Pop
    else:
647 abdf0113 Iustin Pop
      self.minor = minor
648 abdf0113 Iustin Pop
      self.dev_path = self._DevPath(minor)
649 a8083063 Iustin Pop
650 a8083063 Iustin Pop
  @staticmethod
651 abdf0113 Iustin Pop
  def _CheckMetaSize(meta_device):
652 abdf0113 Iustin Pop
    """Check if the given meta device looks like a valid one.
653 a8083063 Iustin Pop

654 abdf0113 Iustin Pop
    This currently only check the size, which must be around
655 abdf0113 Iustin Pop
    128MiB.
656 a8083063 Iustin Pop

657 a8083063 Iustin Pop
    """
658 abdf0113 Iustin Pop
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
659 abdf0113 Iustin Pop
    if result.failed:
660 abdf0113 Iustin Pop
      logger.Error("Failed to get device size: %s - %s" %
661 abdf0113 Iustin Pop
                   (result.fail_reason, result.output))
662 abdf0113 Iustin Pop
      return False
663 a8083063 Iustin Pop
    try:
664 abdf0113 Iustin Pop
      sectors = int(result.stdout)
665 abdf0113 Iustin Pop
    except ValueError:
666 abdf0113 Iustin Pop
      logger.Error("Invalid output from blockdev: '%s'" % result.stdout)
667 abdf0113 Iustin Pop
      return False
668 abdf0113 Iustin Pop
    bytes = sectors * 512
669 abdf0113 Iustin Pop
    if bytes < 128 * 1024 * 1024: # less than 128MiB
670 abdf0113 Iustin Pop
      logger.Error("Meta device too small (%.2fMib)" % (bytes / 1024 / 1024))
671 abdf0113 Iustin Pop
      return False
672 abdf0113 Iustin Pop
    if bytes > (128 + 32) * 1024 * 1024: # account for an extra (big) PE on LVM
673 abdf0113 Iustin Pop
      logger.Error("Meta device too big (%.2fMiB)" % (bytes / 1024 / 1024))
674 abdf0113 Iustin Pop
      return False
675 abdf0113 Iustin Pop
    return True
676 a8083063 Iustin Pop
677 abdf0113 Iustin Pop
  def Rename(self, new_id):
678 abdf0113 Iustin Pop
    """Rename a device.
679 a8083063 Iustin Pop

680 abdf0113 Iustin Pop
    This is not supported for drbd devices.
681 a8083063 Iustin Pop

682 a8083063 Iustin Pop
    """
683 abdf0113 Iustin Pop
    raise errors.ProgrammerError("Can't rename a drbd device")
684 a8083063 Iustin Pop
685 f3e513ad Iustin Pop
686 a2cfdea2 Iustin Pop
class DRBD8(BaseDRBD):
687 a2cfdea2 Iustin Pop
  """DRBD v8.x block device.
688 a2cfdea2 Iustin Pop

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

693 a2cfdea2 Iustin Pop
  The unique_id for the drbd device is the (local_ip, local_port,
694 a2cfdea2 Iustin Pop
  remote_ip, remote_port) tuple, and it must have two children: the
695 a2cfdea2 Iustin Pop
  data device and the meta_device. The meta device is checked for
696 a2cfdea2 Iustin Pop
  valid size and is zeroed on create.
697 a2cfdea2 Iustin Pop

698 a2cfdea2 Iustin Pop
  """
699 a2cfdea2 Iustin Pop
  _MAX_MINORS = 255
700 a2cfdea2 Iustin Pop
  _PARSE_SHOW = None
701 a2cfdea2 Iustin Pop
702 a2cfdea2 Iustin Pop
  def __init__(self, unique_id, children):
703 fc1dc9d7 Iustin Pop
    if children and children.count(None) > 0:
704 fc1dc9d7 Iustin Pop
      children = []
705 a2cfdea2 Iustin Pop
    super(DRBD8, self).__init__(unique_id, children)
706 a2cfdea2 Iustin Pop
    self.major = self._DRBD_MAJOR
707 c3f9340c Guido Trotter
    version = self._GetVersion()
708 c3f9340c Guido Trotter
    if version['k_major'] != 8 :
709 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Mismatch in DRBD kernel version and"
710 a2cfdea2 Iustin Pop
                                    " requested ganeti usage: kernel is"
711 c3f9340c Guido Trotter
                                    " %s.%s, ganeti wants 8.x" %
712 c3f9340c Guido Trotter
                                    (version['k_major'], version['k_minor']))
713 a2cfdea2 Iustin Pop
714 b00b95dd Iustin Pop
    if len(children) not in (0, 2):
715 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(children))
716 a2cfdea2 Iustin Pop
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 4:
717 a2cfdea2 Iustin Pop
      raise ValueError("Invalid configuration data %s" % str(unique_id))
718 a2cfdea2 Iustin Pop
    self._lhost, self._lport, self._rhost, self._rport = unique_id
719 a2cfdea2 Iustin Pop
    self.Attach()
720 a2cfdea2 Iustin Pop
721 a2cfdea2 Iustin Pop
  @classmethod
722 a2cfdea2 Iustin Pop
  def _InitMeta(cls, minor, dev_path):
723 a2cfdea2 Iustin Pop
    """Initialize a meta device.
724 a2cfdea2 Iustin Pop

725 a2cfdea2 Iustin Pop
    This will not work if the given minor is in use.
726 a2cfdea2 Iustin Pop

727 a2cfdea2 Iustin Pop
    """
728 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
729 a2cfdea2 Iustin Pop
                           "v08", dev_path, "0", "create-md"])
730 a2cfdea2 Iustin Pop
    if result.failed:
731 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't initialize meta device: %s" %
732 a2cfdea2 Iustin Pop
                                    result.output)
733 a2cfdea2 Iustin Pop
734 a2cfdea2 Iustin Pop
  @classmethod
735 a2cfdea2 Iustin Pop
  def _FindUnusedMinor(cls):
736 a2cfdea2 Iustin Pop
    """Find an unused DRBD device.
737 a2cfdea2 Iustin Pop

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

741 a2cfdea2 Iustin Pop
    """
742 a2cfdea2 Iustin Pop
    data = cls._GetProcData()
743 a2cfdea2 Iustin Pop
744 a2cfdea2 Iustin Pop
    unused_line = re.compile("^ *([0-9]+): cs:Unconfigured$")
745 a2cfdea2 Iustin Pop
    used_line = re.compile("^ *([0-9]+): cs:")
746 a2cfdea2 Iustin Pop
    highest = None
747 a2cfdea2 Iustin Pop
    for line in data:
748 a2cfdea2 Iustin Pop
      match = unused_line.match(line)
749 a2cfdea2 Iustin Pop
      if match:
750 a2cfdea2 Iustin Pop
        return int(match.group(1))
751 a2cfdea2 Iustin Pop
      match = used_line.match(line)
752 a2cfdea2 Iustin Pop
      if match:
753 a2cfdea2 Iustin Pop
        minor = int(match.group(1))
754 a2cfdea2 Iustin Pop
        highest = max(highest, minor)
755 a2cfdea2 Iustin Pop
    if highest is None: # there are no minors in use at all
756 a2cfdea2 Iustin Pop
      return 0
757 a2cfdea2 Iustin Pop
    if highest >= cls._MAX_MINORS:
758 a2cfdea2 Iustin Pop
      logger.Error("Error: no free drbd minors!")
759 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
760 a2cfdea2 Iustin Pop
    return highest + 1
761 a2cfdea2 Iustin Pop
762 a2cfdea2 Iustin Pop
  @classmethod
763 a2cfdea2 Iustin Pop
  def _IsValidMeta(cls, meta_device):
764 a2cfdea2 Iustin Pop
    """Check if the given meta device looks like a valid one.
765 a2cfdea2 Iustin Pop

766 a2cfdea2 Iustin Pop
    """
767 a2cfdea2 Iustin Pop
    minor = cls._FindUnusedMinor()
768 a2cfdea2 Iustin Pop
    minor_path = cls._DevPath(minor)
769 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdmeta", minor_path,
770 a2cfdea2 Iustin Pop
                           "v08", meta_device, "0",
771 a2cfdea2 Iustin Pop
                           "dstate"])
772 a2cfdea2 Iustin Pop
    if result.failed:
773 a2cfdea2 Iustin Pop
      logger.Error("Invalid meta device %s: %s" % (meta_device, result.output))
774 a2cfdea2 Iustin Pop
      return False
775 a2cfdea2 Iustin Pop
    return True
776 a2cfdea2 Iustin Pop
777 a2cfdea2 Iustin Pop
  @classmethod
778 a2cfdea2 Iustin Pop
  def _GetShowParser(cls):
779 a2cfdea2 Iustin Pop
    """Return a parser for `drbd show` output.
780 a2cfdea2 Iustin Pop

781 a2cfdea2 Iustin Pop
    This will either create or return an already-create parser for the
782 a2cfdea2 Iustin Pop
    output of the command `drbd show`.
783 a2cfdea2 Iustin Pop

784 a2cfdea2 Iustin Pop
    """
785 a2cfdea2 Iustin Pop
    if cls._PARSE_SHOW is not None:
786 a2cfdea2 Iustin Pop
      return cls._PARSE_SHOW
787 a2cfdea2 Iustin Pop
788 a2cfdea2 Iustin Pop
    # pyparsing setup
789 a2cfdea2 Iustin Pop
    lbrace = pyp.Literal("{").suppress()
790 a2cfdea2 Iustin Pop
    rbrace = pyp.Literal("}").suppress()
791 a2cfdea2 Iustin Pop
    semi = pyp.Literal(";").suppress()
792 a2cfdea2 Iustin Pop
    # this also converts the value to an int
793 c522ea02 Iustin Pop
    number = pyp.Word(pyp.nums).setParseAction(lambda s, l, t: int(t[0]))
794 a2cfdea2 Iustin Pop
795 a2cfdea2 Iustin Pop
    comment = pyp.Literal ("#") + pyp.Optional(pyp.restOfLine)
796 a2cfdea2 Iustin Pop
    defa = pyp.Literal("_is_default").suppress()
797 a2cfdea2 Iustin Pop
    dbl_quote = pyp.Literal('"').suppress()
798 a2cfdea2 Iustin Pop
799 a2cfdea2 Iustin Pop
    keyword = pyp.Word(pyp.alphanums + '-')
800 a2cfdea2 Iustin Pop
801 a2cfdea2 Iustin Pop
    # value types
802 a2cfdea2 Iustin Pop
    value = pyp.Word(pyp.alphanums + '_-/.:')
803 a2cfdea2 Iustin Pop
    quoted = dbl_quote + pyp.CharsNotIn('"') + dbl_quote
804 a2cfdea2 Iustin Pop
    addr_port = (pyp.Word(pyp.nums + '.') + pyp.Literal(':').suppress() +
805 a2cfdea2 Iustin Pop
                 number)
806 a2cfdea2 Iustin Pop
    # meta device, extended syntax
807 a2cfdea2 Iustin Pop
    meta_value = ((value ^ quoted) + pyp.Literal('[').suppress() +
808 a2cfdea2 Iustin Pop
                  number + pyp.Word(']').suppress())
809 a2cfdea2 Iustin Pop
810 a2cfdea2 Iustin Pop
    # a statement
811 a2cfdea2 Iustin Pop
    stmt = (~rbrace + keyword + ~lbrace +
812 63012024 Guido Trotter
            pyp.Optional(addr_port ^ value ^ quoted ^ meta_value) +
813 a2cfdea2 Iustin Pop
            pyp.Optional(defa) + semi +
814 a2cfdea2 Iustin Pop
            pyp.Optional(pyp.restOfLine).suppress())
815 a2cfdea2 Iustin Pop
816 a2cfdea2 Iustin Pop
    # an entire section
817 a2cfdea2 Iustin Pop
    section_name = pyp.Word(pyp.alphas + '_')
818 a2cfdea2 Iustin Pop
    section = section_name + lbrace + pyp.ZeroOrMore(pyp.Group(stmt)) + rbrace
819 a2cfdea2 Iustin Pop
820 a2cfdea2 Iustin Pop
    bnf = pyp.ZeroOrMore(pyp.Group(section ^ stmt))
821 a2cfdea2 Iustin Pop
    bnf.ignore(comment)
822 a2cfdea2 Iustin Pop
823 a2cfdea2 Iustin Pop
    cls._PARSE_SHOW = bnf
824 a2cfdea2 Iustin Pop
825 a2cfdea2 Iustin Pop
    return bnf
826 a2cfdea2 Iustin Pop
827 a2cfdea2 Iustin Pop
  @classmethod
828 3840729d Iustin Pop
  def _GetShowData(cls, minor):
829 3840729d Iustin Pop
    """Return the `drbdsetup show` data for a minor.
830 a2cfdea2 Iustin Pop

831 a2cfdea2 Iustin Pop
    """
832 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "show"])
833 a2cfdea2 Iustin Pop
    if result.failed:
834 6c896e2f Iustin Pop
      logger.Error("Can't display the drbd config: %s - %s" %
835 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
836 3840729d Iustin Pop
      return None
837 3840729d Iustin Pop
    return result.stdout
838 3840729d Iustin Pop
839 3840729d Iustin Pop
  @classmethod
840 3840729d Iustin Pop
  def _GetDevInfo(cls, out):
841 3840729d Iustin Pop
    """Parse details about a given DRBD minor.
842 3840729d Iustin Pop

843 3840729d Iustin Pop
    This return, if available, the local backing device (as a path)
844 3840729d Iustin Pop
    and the local and remote (ip, port) information from a string
845 3840729d Iustin Pop
    containing the output of the `drbdsetup show` command as returned
846 3840729d Iustin Pop
    by _GetShowData.
847 3840729d Iustin Pop

848 3840729d Iustin Pop
    """
849 3840729d Iustin Pop
    data = {}
850 a2cfdea2 Iustin Pop
    if not out:
851 a2cfdea2 Iustin Pop
      return data
852 a2cfdea2 Iustin Pop
853 a2cfdea2 Iustin Pop
    bnf = cls._GetShowParser()
854 a2cfdea2 Iustin Pop
    # run pyparse
855 a2cfdea2 Iustin Pop
856 a2cfdea2 Iustin Pop
    try:
857 a2cfdea2 Iustin Pop
      results = bnf.parseString(out)
858 a2cfdea2 Iustin Pop
    except pyp.ParseException, err:
859 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't parse drbdsetup show output: %s" %
860 a2cfdea2 Iustin Pop
                                    str(err))
861 a2cfdea2 Iustin Pop
862 a2cfdea2 Iustin Pop
    # and massage the results into our desired format
863 a2cfdea2 Iustin Pop
    for section in results:
864 a2cfdea2 Iustin Pop
      sname = section[0]
865 a2cfdea2 Iustin Pop
      if sname == "_this_host":
866 a2cfdea2 Iustin Pop
        for lst in section[1:]:
867 a2cfdea2 Iustin Pop
          if lst[0] == "disk":
868 a2cfdea2 Iustin Pop
            data["local_dev"] = lst[1]
869 a2cfdea2 Iustin Pop
          elif lst[0] == "meta-disk":
870 a2cfdea2 Iustin Pop
            data["meta_dev"] = lst[1]
871 a2cfdea2 Iustin Pop
            data["meta_index"] = lst[2]
872 a2cfdea2 Iustin Pop
          elif lst[0] == "address":
873 a2cfdea2 Iustin Pop
            data["local_addr"] = tuple(lst[1:])
874 a2cfdea2 Iustin Pop
      elif sname == "_remote_host":
875 a2cfdea2 Iustin Pop
        for lst in section[1:]:
876 a2cfdea2 Iustin Pop
          if lst[0] == "address":
877 a2cfdea2 Iustin Pop
            data["remote_addr"] = tuple(lst[1:])
878 a2cfdea2 Iustin Pop
    return data
879 a2cfdea2 Iustin Pop
880 a2cfdea2 Iustin Pop
  def _MatchesLocal(self, info):
881 a2cfdea2 Iustin Pop
    """Test if our local config matches with an existing device.
882 a2cfdea2 Iustin Pop

883 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
884 a2cfdea2 Iustin Pop
    method tests if our local backing device is the same as the one in
885 a2cfdea2 Iustin Pop
    the info parameter, in effect testing if we look like the given
886 a2cfdea2 Iustin Pop
    device.
887 a2cfdea2 Iustin Pop

888 a2cfdea2 Iustin Pop
    """
889 b00b95dd Iustin Pop
    if self._children:
890 b00b95dd Iustin Pop
      backend, meta = self._children
891 b00b95dd Iustin Pop
    else:
892 b00b95dd Iustin Pop
      backend = meta = None
893 b00b95dd Iustin Pop
894 a2cfdea2 Iustin Pop
    if backend is not None:
895 b00b95dd Iustin Pop
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
896 a2cfdea2 Iustin Pop
    else:
897 a2cfdea2 Iustin Pop
      retval = ("local_dev" not in info)
898 b00b95dd Iustin Pop
899 a2cfdea2 Iustin Pop
    if meta is not None:
900 b00b95dd Iustin Pop
      retval = retval and ("meta_dev" in info and
901 b00b95dd Iustin Pop
                           info["meta_dev"] == meta.dev_path)
902 b00b95dd Iustin Pop
      retval = retval and ("meta_index" in info and
903 b00b95dd Iustin Pop
                           info["meta_index"] == 0)
904 a2cfdea2 Iustin Pop
    else:
905 a2cfdea2 Iustin Pop
      retval = retval and ("meta_dev" not in info and
906 a2cfdea2 Iustin Pop
                           "meta_index" not in info)
907 a2cfdea2 Iustin Pop
    return retval
908 a2cfdea2 Iustin Pop
909 a2cfdea2 Iustin Pop
  def _MatchesNet(self, info):
910 a2cfdea2 Iustin Pop
    """Test if our network config matches with an existing device.
911 a2cfdea2 Iustin Pop

912 a2cfdea2 Iustin Pop
    The parameter should be as returned from `_GetDevInfo()`. This
913 a2cfdea2 Iustin Pop
    method tests if our network configuration is the same as the one
914 a2cfdea2 Iustin Pop
    in the info parameter, in effect testing if we look like the given
915 a2cfdea2 Iustin Pop
    device.
916 a2cfdea2 Iustin Pop

917 a2cfdea2 Iustin Pop
    """
918 a2cfdea2 Iustin Pop
    if (((self._lhost is None and not ("local_addr" in info)) and
919 a2cfdea2 Iustin Pop
         (self._rhost is None and not ("remote_addr" in info)))):
920 a2cfdea2 Iustin Pop
      return True
921 a2cfdea2 Iustin Pop
922 a2cfdea2 Iustin Pop
    if self._lhost is None:
923 a2cfdea2 Iustin Pop
      return False
924 a2cfdea2 Iustin Pop
925 a2cfdea2 Iustin Pop
    if not ("local_addr" in info and
926 a2cfdea2 Iustin Pop
            "remote_addr" in info):
927 a2cfdea2 Iustin Pop
      return False
928 a2cfdea2 Iustin Pop
929 a2cfdea2 Iustin Pop
    retval = (info["local_addr"] == (self._lhost, self._lport))
930 a2cfdea2 Iustin Pop
    retval = (retval and
931 a2cfdea2 Iustin Pop
              info["remote_addr"] == (self._rhost, self._rport))
932 a2cfdea2 Iustin Pop
    return retval
933 a2cfdea2 Iustin Pop
934 a2cfdea2 Iustin Pop
  @classmethod
935 a2cfdea2 Iustin Pop
  def _AssembleLocal(cls, minor, backend, meta):
936 a2cfdea2 Iustin Pop
    """Configure the local part of a DRBD device.
937 a2cfdea2 Iustin Pop

938 a2cfdea2 Iustin Pop
    This is the first thing that must be done on an unconfigured DRBD
939 a2cfdea2 Iustin Pop
    device. And it must be done only once.
940 a2cfdea2 Iustin Pop

941 a2cfdea2 Iustin Pop
    """
942 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta):
943 a2cfdea2 Iustin Pop
      return False
944 333411a7 Guido Trotter
    args = ["drbdsetup", cls._DevPath(minor), "disk",
945 333411a7 Guido Trotter
            backend, meta, "0", "-e", "detach", "--create-device"]
946 333411a7 Guido Trotter
    result = utils.RunCmd(args)
947 a2cfdea2 Iustin Pop
    if result.failed:
948 a2cfdea2 Iustin Pop
      logger.Error("Can't attach local disk: %s" % result.output)
949 a2cfdea2 Iustin Pop
    return not result.failed
950 a2cfdea2 Iustin Pop
951 a2cfdea2 Iustin Pop
  @classmethod
952 a2cfdea2 Iustin Pop
  def _AssembleNet(cls, minor, net_info, protocol,
953 a2cfdea2 Iustin Pop
                   dual_pri=False, hmac=None, secret=None):
954 a2cfdea2 Iustin Pop
    """Configure the network part of the device.
955 a2cfdea2 Iustin Pop

956 a2cfdea2 Iustin Pop
    """
957 a2cfdea2 Iustin Pop
    lhost, lport, rhost, rport = net_info
958 52857176 Iustin Pop
    if None in net_info:
959 52857176 Iustin Pop
      # we don't want network connection and actually want to make
960 52857176 Iustin Pop
      # sure its shutdown
961 52857176 Iustin Pop
      return cls._ShutdownNet(minor)
962 52857176 Iustin Pop
963 a2cfdea2 Iustin Pop
    args = ["drbdsetup", cls._DevPath(minor), "net",
964 f38478b2 Iustin Pop
            "%s:%s" % (lhost, lport), "%s:%s" % (rhost, rport), protocol,
965 f38478b2 Iustin Pop
            "-A", "discard-zero-changes",
966 f38478b2 Iustin Pop
            "-B", "consensus",
967 ab6cc81c Iustin Pop
            "--create-device",
968 f38478b2 Iustin Pop
            ]
969 a2cfdea2 Iustin Pop
    if dual_pri:
970 a2cfdea2 Iustin Pop
      args.append("-m")
971 a2cfdea2 Iustin Pop
    if hmac and secret:
972 a2cfdea2 Iustin Pop
      args.extend(["-a", hmac, "-x", secret])
973 a2cfdea2 Iustin Pop
    result = utils.RunCmd(args)
974 a2cfdea2 Iustin Pop
    if result.failed:
975 6c896e2f Iustin Pop
      logger.Error("Can't setup network for dbrd device: %s - %s" %
976 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
977 a2cfdea2 Iustin Pop
      return False
978 a2cfdea2 Iustin Pop
979 a2cfdea2 Iustin Pop
    timeout = time.time() + 10
980 a2cfdea2 Iustin Pop
    ok = False
981 a2cfdea2 Iustin Pop
    while time.time() < timeout:
982 3840729d Iustin Pop
      info = cls._GetDevInfo(cls._GetShowData(minor))
983 a2cfdea2 Iustin Pop
      if not "local_addr" in info or not "remote_addr" in info:
984 a2cfdea2 Iustin Pop
        time.sleep(1)
985 a2cfdea2 Iustin Pop
        continue
986 a2cfdea2 Iustin Pop
      if (info["local_addr"] != (lhost, lport) or
987 a2cfdea2 Iustin Pop
          info["remote_addr"] != (rhost, rport)):
988 a2cfdea2 Iustin Pop
        time.sleep(1)
989 a2cfdea2 Iustin Pop
        continue
990 a2cfdea2 Iustin Pop
      ok = True
991 a2cfdea2 Iustin Pop
      break
992 a2cfdea2 Iustin Pop
    if not ok:
993 a2cfdea2 Iustin Pop
      logger.Error("Timeout while configuring network")
994 a2cfdea2 Iustin Pop
      return False
995 a2cfdea2 Iustin Pop
    return True
996 a2cfdea2 Iustin Pop
997 b00b95dd Iustin Pop
  def AddChildren(self, devices):
998 b00b95dd Iustin Pop
    """Add a disk to the DRBD device.
999 b00b95dd Iustin Pop

1000 b00b95dd Iustin Pop
    """
1001 b00b95dd Iustin Pop
    if self.minor is None:
1002 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to dbrd8 during AddChildren")
1003 b00b95dd Iustin Pop
    if len(devices) != 2:
1004 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Need two devices for AddChildren")
1005 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1006 03ece5f3 Iustin Pop
    if "local_dev" in info:
1007 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("DRBD8 already attached to a local disk")
1008 b00b95dd Iustin Pop
    backend, meta = devices
1009 b00b95dd Iustin Pop
    if backend.dev_path is None or meta.dev_path is None:
1010 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Children not ready during AddChildren")
1011 b00b95dd Iustin Pop
    backend.Open()
1012 b00b95dd Iustin Pop
    meta.Open()
1013 b00b95dd Iustin Pop
    if not self._CheckMetaSize(meta.dev_path):
1014 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1015 b00b95dd Iustin Pop
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
1016 b00b95dd Iustin Pop
    if not self._IsValidMeta(meta.dev_path):
1017 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1018 b00b95dd Iustin Pop
1019 b00b95dd Iustin Pop
    if not self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path):
1020 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to local storage")
1021 b00b95dd Iustin Pop
    self._children = devices
1022 b00b95dd Iustin Pop
1023 b00b95dd Iustin Pop
  def RemoveChildren(self, devices):
1024 b00b95dd Iustin Pop
    """Detach the drbd device from local storage.
1025 b00b95dd Iustin Pop

1026 b00b95dd Iustin Pop
    """
1027 b00b95dd Iustin Pop
    if self.minor is None:
1028 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't attach to drbd8 during"
1029 b00b95dd Iustin Pop
                                    " RemoveChildren")
1030 03ece5f3 Iustin Pop
    # early return if we don't actually have backing storage
1031 3840729d Iustin Pop
    info = self._GetDevInfo(self._GetShowData(self.minor))
1032 03ece5f3 Iustin Pop
    if "local_dev" not in info:
1033 03ece5f3 Iustin Pop
      return
1034 b00b95dd Iustin Pop
    if len(self._children) != 2:
1035 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We don't have two children: %s" %
1036 b00b95dd Iustin Pop
                                    self._children)
1037 e739bd57 Iustin Pop
    if self._children.count(None) == 2: # we don't actually have children :)
1038 e739bd57 Iustin Pop
      logger.Error("Requested detach while detached")
1039 e739bd57 Iustin Pop
      return
1040 b00b95dd Iustin Pop
    if len(devices) != 2:
1041 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("We need two children in RemoveChildren")
1042 e739bd57 Iustin Pop
    for child, dev in zip(self._children, devices):
1043 e739bd57 Iustin Pop
      if dev != child.dev_path:
1044 e739bd57 Iustin Pop
        raise errors.BlockDeviceError("Mismatch in local storage"
1045 e739bd57 Iustin Pop
                                      " (%s != %s) in RemoveChildren" %
1046 e739bd57 Iustin Pop
                                      (dev, child.dev_path))
1047 b00b95dd Iustin Pop
1048 b00b95dd Iustin Pop
    if not self._ShutdownLocal(self.minor):
1049 b00b95dd Iustin Pop
      raise errors.BlockDeviceError("Can't detach from local storage")
1050 b00b95dd Iustin Pop
    self._children = []
1051 b00b95dd Iustin Pop
1052 a2cfdea2 Iustin Pop
  def SetSyncSpeed(self, kbytes):
1053 a2cfdea2 Iustin Pop
    """Set the speed of the DRBD syncer.
1054 a2cfdea2 Iustin Pop

1055 a2cfdea2 Iustin Pop
    """
1056 a2cfdea2 Iustin Pop
    children_result = super(DRBD8, self).SetSyncSpeed(kbytes)
1057 a2cfdea2 Iustin Pop
    if self.minor is None:
1058 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1059 a2cfdea2 Iustin Pop
      return False
1060 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "syncer", "-r", "%d" %
1061 a2cfdea2 Iustin Pop
                           kbytes])
1062 a2cfdea2 Iustin Pop
    if result.failed:
1063 6c896e2f Iustin Pop
      logger.Error("Can't change syncer rate: %s - %s" %
1064 6c896e2f Iustin Pop
                   (result.fail_reason, result.output))
1065 a2cfdea2 Iustin Pop
    return not result.failed and children_result
1066 a2cfdea2 Iustin Pop
1067 a2cfdea2 Iustin Pop
  def GetSyncStatus(self):
1068 a2cfdea2 Iustin Pop
    """Returns the sync status of the device.
1069 a2cfdea2 Iustin Pop

1070 a2cfdea2 Iustin Pop
    Returns:
1071 583e3f6f Iustin Pop
     (sync_percent, estimated_time, is_degraded)
1072 a2cfdea2 Iustin Pop

1073 a2cfdea2 Iustin Pop
    If sync_percent is None, it means all is ok
1074 a2cfdea2 Iustin Pop
    If estimated_time is None, it means we can't esimate
1075 0834c866 Iustin Pop
    the time needed, otherwise it's the time left in seconds.
1076 0834c866 Iustin Pop

1077 0834c866 Iustin Pop

1078 0834c866 Iustin Pop
    We set the is_degraded parameter to True on two conditions:
1079 0834c866 Iustin Pop
    network not connected or local disk missing.
1080 0834c866 Iustin Pop

1081 0834c866 Iustin Pop
    We compute the ldisk parameter based on wheter we have a local
1082 0834c866 Iustin Pop
    disk or not.
1083 a2cfdea2 Iustin Pop

1084 a2cfdea2 Iustin Pop
    """
1085 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1086 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to device in GetSyncStatus")
1087 a2cfdea2 Iustin Pop
    proc_info = self._MassageProcData(self._GetProcData())
1088 a2cfdea2 Iustin Pop
    if self.minor not in proc_info:
1089 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find myself in /proc (minor %d)" %
1090 a2cfdea2 Iustin Pop
                                    self.minor)
1091 a2cfdea2 Iustin Pop
    line = proc_info[self.minor]
1092 a2cfdea2 Iustin Pop
    match = re.match("^.*sync'ed: *([0-9.]+)%.*"
1093 a2cfdea2 Iustin Pop
                     " finish: ([0-9]+):([0-9]+):([0-9]+) .*$", line)
1094 a2cfdea2 Iustin Pop
    if match:
1095 a2cfdea2 Iustin Pop
      sync_percent = float(match.group(1))
1096 a2cfdea2 Iustin Pop
      hours = int(match.group(2))
1097 a2cfdea2 Iustin Pop
      minutes = int(match.group(3))
1098 a2cfdea2 Iustin Pop
      seconds = int(match.group(4))
1099 a2cfdea2 Iustin Pop
      est_time = hours * 3600 + minutes * 60 + seconds
1100 a2cfdea2 Iustin Pop
    else:
1101 a2cfdea2 Iustin Pop
      sync_percent = None
1102 a2cfdea2 Iustin Pop
      est_time = None
1103 583e3f6f Iustin Pop
    match = re.match("^ *\d+: cs:(\w+).*ds:(\w+)/(\w+).*$", line)
1104 a2cfdea2 Iustin Pop
    if not match:
1105 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find my data in /proc (minor %d)" %
1106 a2cfdea2 Iustin Pop
                                    self.minor)
1107 a2cfdea2 Iustin Pop
    client_state = match.group(1)
1108 583e3f6f Iustin Pop
    local_disk_state = match.group(2)
1109 0834c866 Iustin Pop
    ldisk = local_disk_state != "UpToDate"
1110 0834c866 Iustin Pop
    is_degraded = client_state != "Connected"
1111 0834c866 Iustin Pop
    return sync_percent, est_time, is_degraded or ldisk, ldisk
1112 a2cfdea2 Iustin Pop
1113 a2cfdea2 Iustin Pop
  def Open(self, force=False):
1114 a2cfdea2 Iustin Pop
    """Make the local state primary.
1115 a2cfdea2 Iustin Pop

1116 f860ff4e Guido Trotter
    If the 'force' parameter is given, the '-o' option is passed to
1117 f860ff4e Guido Trotter
    drbdsetup. Since this is a potentially dangerous operation, the
1118 a2cfdea2 Iustin Pop
    force flag should be only given after creation, when it actually
1119 f860ff4e Guido Trotter
    is mandatory.
1120 a2cfdea2 Iustin Pop

1121 a2cfdea2 Iustin Pop
    """
1122 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1123 a2cfdea2 Iustin Pop
      logger.Error("DRBD cannot attach to a device during open")
1124 a2cfdea2 Iustin Pop
      return False
1125 a2cfdea2 Iustin Pop
    cmd = ["drbdsetup", self.dev_path, "primary"]
1126 a2cfdea2 Iustin Pop
    if force:
1127 a2cfdea2 Iustin Pop
      cmd.append("-o")
1128 a2cfdea2 Iustin Pop
    result = utils.RunCmd(cmd)
1129 a2cfdea2 Iustin Pop
    if result.failed:
1130 fdbd668d Iustin Pop
      msg = ("Can't make drbd device primary: %s" % result.output)
1131 fdbd668d Iustin Pop
      logger.Error(msg)
1132 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1133 a2cfdea2 Iustin Pop
1134 a2cfdea2 Iustin Pop
  def Close(self):
1135 a2cfdea2 Iustin Pop
    """Make the local state secondary.
1136 a2cfdea2 Iustin Pop

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

1139 a2cfdea2 Iustin Pop
    """
1140 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1141 a2cfdea2 Iustin Pop
      logger.Info("Instance not attached to a device")
1142 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't find device")
1143 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", self.dev_path, "secondary"])
1144 a2cfdea2 Iustin Pop
    if result.failed:
1145 fdbd668d Iustin Pop
      msg = ("Can't switch drbd device to"
1146 fdbd668d Iustin Pop
             " secondary: %s" % result.output)
1147 fdbd668d Iustin Pop
      logger.Error(msg)
1148 fdbd668d Iustin Pop
      raise errors.BlockDeviceError(msg)
1149 a2cfdea2 Iustin Pop
1150 a2cfdea2 Iustin Pop
  def Attach(self):
1151 a2cfdea2 Iustin Pop
    """Find a DRBD device which matches our config and attach to it.
1152 a2cfdea2 Iustin Pop

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

1157 a2cfdea2 Iustin Pop
    """
1158 a2cfdea2 Iustin Pop
    for minor in self._GetUsedDevs():
1159 3840729d Iustin Pop
      info = self._GetDevInfo(self._GetShowData(minor))
1160 a2cfdea2 Iustin Pop
      match_l = self._MatchesLocal(info)
1161 a2cfdea2 Iustin Pop
      match_r = self._MatchesNet(info)
1162 a2cfdea2 Iustin Pop
      if match_l and match_r:
1163 a2cfdea2 Iustin Pop
        break
1164 a2cfdea2 Iustin Pop
      if match_l and not match_r and "local_addr" not in info:
1165 a2cfdea2 Iustin Pop
        res_r = self._AssembleNet(minor,
1166 a2cfdea2 Iustin Pop
                                  (self._lhost, self._lport,
1167 a2cfdea2 Iustin Pop
                                   self._rhost, self._rport),
1168 a2cfdea2 Iustin Pop
                                  "C")
1169 3840729d Iustin Pop
        if res_r:
1170 3840729d Iustin Pop
          if self._MatchesNet(self._GetDevInfo(self._GetShowData(minor))):
1171 3840729d Iustin Pop
            break
1172 fc1dc9d7 Iustin Pop
      # the weakest case: we find something that is only net attached
1173 fc1dc9d7 Iustin Pop
      # even though we were passed some children at init time
1174 fc1dc9d7 Iustin Pop
      if match_r and "local_dev" not in info:
1175 fc1dc9d7 Iustin Pop
        break
1176 bf25af3b Iustin Pop
1177 bf25af3b Iustin Pop
      # this case must be considered only if we actually have local
1178 bf25af3b Iustin Pop
      # storage, i.e. not in diskless mode, because all diskless
1179 bf25af3b Iustin Pop
      # devices are equal from the point of view of local
1180 bf25af3b Iustin Pop
      # configuration
1181 bf25af3b Iustin Pop
      if (match_l and "local_dev" in info and
1182 bf25af3b Iustin Pop
          not match_r and "local_addr" in info):
1183 9cdbe77f Iustin Pop
        # strange case - the device network part points to somewhere
1184 9cdbe77f Iustin Pop
        # else, even though its local storage is ours; as we own the
1185 9cdbe77f Iustin Pop
        # drbd space, we try to disconnect from the remote peer and
1186 9cdbe77f Iustin Pop
        # reconnect to our correct one
1187 9cdbe77f Iustin Pop
        if not self._ShutdownNet(minor):
1188 9cdbe77f Iustin Pop
          raise errors.BlockDeviceError("Device has correct local storage,"
1189 9cdbe77f Iustin Pop
                                        " wrong remote peer and is unable to"
1190 9cdbe77f Iustin Pop
                                        " disconnect in order to attach to"
1191 9cdbe77f Iustin Pop
                                        " the correct peer")
1192 9cdbe77f Iustin Pop
        # note: _AssembleNet also handles the case when we don't want
1193 9cdbe77f Iustin Pop
        # local storage (i.e. one or more of the _[lr](host|port) is
1194 9cdbe77f Iustin Pop
        # None)
1195 9cdbe77f Iustin Pop
        if (self._AssembleNet(minor, (self._lhost, self._lport,
1196 9cdbe77f Iustin Pop
                                      self._rhost, self._rport), "C") and
1197 3840729d Iustin Pop
            self._MatchesNet(self._GetDevInfo(self._GetShowData(minor)))):
1198 9cdbe77f Iustin Pop
          break
1199 9cdbe77f Iustin Pop
1200 a2cfdea2 Iustin Pop
    else:
1201 a2cfdea2 Iustin Pop
      minor = None
1202 a2cfdea2 Iustin Pop
1203 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1204 a2cfdea2 Iustin Pop
    return minor is not None
1205 a2cfdea2 Iustin Pop
1206 a2cfdea2 Iustin Pop
  def Assemble(self):
1207 a2cfdea2 Iustin Pop
    """Assemble the drbd.
1208 a2cfdea2 Iustin Pop

1209 a2cfdea2 Iustin Pop
    Method:
1210 a2cfdea2 Iustin Pop
      - if we have a local backing device, we bind to it by:
1211 a2cfdea2 Iustin Pop
        - checking the list of used drbd devices
1212 a2cfdea2 Iustin Pop
        - check if the local minor use of any of them is our own device
1213 a2cfdea2 Iustin Pop
        - if yes, abort?
1214 a2cfdea2 Iustin Pop
        - if not, bind
1215 a2cfdea2 Iustin Pop
      - if we have a local/remote net info:
1216 a2cfdea2 Iustin Pop
        - redo the local backing device step for the remote device
1217 a2cfdea2 Iustin Pop
        - check if any drbd device is using the local port,
1218 a2cfdea2 Iustin Pop
          if yes abort
1219 a2cfdea2 Iustin Pop
        - check if any remote drbd device is using the remote
1220 a2cfdea2 Iustin Pop
          port, if yes abort (for now)
1221 a2cfdea2 Iustin Pop
        - bind our net port
1222 a2cfdea2 Iustin Pop
        - bind the remote net port
1223 a2cfdea2 Iustin Pop

1224 a2cfdea2 Iustin Pop
    """
1225 a2cfdea2 Iustin Pop
    self.Attach()
1226 a2cfdea2 Iustin Pop
    if self.minor is not None:
1227 a2cfdea2 Iustin Pop
      logger.Info("Already assembled")
1228 a2cfdea2 Iustin Pop
      return True
1229 a2cfdea2 Iustin Pop
1230 a2cfdea2 Iustin Pop
    result = super(DRBD8, self).Assemble()
1231 a2cfdea2 Iustin Pop
    if not result:
1232 a2cfdea2 Iustin Pop
      return result
1233 a2cfdea2 Iustin Pop
1234 a2cfdea2 Iustin Pop
    minor = self._FindUnusedMinor()
1235 a2cfdea2 Iustin Pop
    need_localdev_teardown = False
1236 fc1dc9d7 Iustin Pop
    if self._children and self._children[0] and self._children[1]:
1237 a2cfdea2 Iustin Pop
      result = self._AssembleLocal(minor, self._children[0].dev_path,
1238 a2cfdea2 Iustin Pop
                                   self._children[1].dev_path)
1239 a2cfdea2 Iustin Pop
      if not result:
1240 a2cfdea2 Iustin Pop
        return False
1241 a2cfdea2 Iustin Pop
      need_localdev_teardown = True
1242 a2cfdea2 Iustin Pop
    if self._lhost and self._lport and self._rhost and self._rport:
1243 a2cfdea2 Iustin Pop
      result = self._AssembleNet(minor,
1244 a2cfdea2 Iustin Pop
                                 (self._lhost, self._lport,
1245 a2cfdea2 Iustin Pop
                                  self._rhost, self._rport),
1246 a2cfdea2 Iustin Pop
                                 "C")
1247 a2cfdea2 Iustin Pop
      if not result:
1248 a2cfdea2 Iustin Pop
        if need_localdev_teardown:
1249 a2cfdea2 Iustin Pop
          # we will ignore failures from this
1250 a2cfdea2 Iustin Pop
          logger.Error("net setup failed, tearing down local device")
1251 a2cfdea2 Iustin Pop
          self._ShutdownAll(minor)
1252 a2cfdea2 Iustin Pop
        return False
1253 a2cfdea2 Iustin Pop
    self._SetFromMinor(minor)
1254 a2cfdea2 Iustin Pop
    return True
1255 a2cfdea2 Iustin Pop
1256 a2cfdea2 Iustin Pop
  @classmethod
1257 b00b95dd Iustin Pop
  def _ShutdownLocal(cls, minor):
1258 b00b95dd Iustin Pop
    """Detach from the local device.
1259 b00b95dd Iustin Pop

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

1263 b00b95dd Iustin Pop
    """
1264 b00b95dd Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "detach"])
1265 b00b95dd Iustin Pop
    if result.failed:
1266 b00b95dd Iustin Pop
      logger.Error("Can't detach local device: %s" % result.output)
1267 b00b95dd Iustin Pop
    return not result.failed
1268 b00b95dd Iustin Pop
1269 b00b95dd Iustin Pop
  @classmethod
1270 f3e513ad Iustin Pop
  def _ShutdownNet(cls, minor):
1271 f3e513ad Iustin Pop
    """Disconnect from the remote peer.
1272 f3e513ad Iustin Pop

1273 f3e513ad Iustin Pop
    This fails if we don't have a local device.
1274 f3e513ad Iustin Pop

1275 f3e513ad Iustin Pop
    """
1276 f3e513ad Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "disconnect"])
1277 a8459f1c Iustin Pop
    if result.failed:
1278 a8459f1c Iustin Pop
      logger.Error("Can't shutdown network: %s" % result.output)
1279 f3e513ad Iustin Pop
    return not result.failed
1280 f3e513ad Iustin Pop
1281 f3e513ad Iustin Pop
  @classmethod
1282 a2cfdea2 Iustin Pop
  def _ShutdownAll(cls, minor):
1283 a2cfdea2 Iustin Pop
    """Deactivate the device.
1284 a2cfdea2 Iustin Pop

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

1287 a2cfdea2 Iustin Pop
    """
1288 a2cfdea2 Iustin Pop
    result = utils.RunCmd(["drbdsetup", cls._DevPath(minor), "down"])
1289 a2cfdea2 Iustin Pop
    if result.failed:
1290 a2cfdea2 Iustin Pop
      logger.Error("Can't shutdown drbd device: %s" % result.output)
1291 a2cfdea2 Iustin Pop
    return not result.failed
1292 a2cfdea2 Iustin Pop
1293 a2cfdea2 Iustin Pop
  def Shutdown(self):
1294 a2cfdea2 Iustin Pop
    """Shutdown the DRBD device.
1295 a2cfdea2 Iustin Pop

1296 a2cfdea2 Iustin Pop
    """
1297 a2cfdea2 Iustin Pop
    if self.minor is None and not self.Attach():
1298 a2cfdea2 Iustin Pop
      logger.Info("DRBD device not attached to a device during Shutdown")
1299 a2cfdea2 Iustin Pop
      return True
1300 a2cfdea2 Iustin Pop
    if not self._ShutdownAll(self.minor):
1301 a2cfdea2 Iustin Pop
      return False
1302 a2cfdea2 Iustin Pop
    self.minor = None
1303 a2cfdea2 Iustin Pop
    self.dev_path = None
1304 a2cfdea2 Iustin Pop
    return True
1305 a2cfdea2 Iustin Pop
1306 a2cfdea2 Iustin Pop
  def Remove(self):
1307 a2cfdea2 Iustin Pop
    """Stub remove for DRBD devices.
1308 a2cfdea2 Iustin Pop

1309 a2cfdea2 Iustin Pop
    """
1310 a2cfdea2 Iustin Pop
    return self.Shutdown()
1311 a2cfdea2 Iustin Pop
1312 a2cfdea2 Iustin Pop
  @classmethod
1313 a2cfdea2 Iustin Pop
  def Create(cls, unique_id, children, size):
1314 a2cfdea2 Iustin Pop
    """Create a new DRBD8 device.
1315 a2cfdea2 Iustin Pop

1316 a2cfdea2 Iustin Pop
    Since DRBD devices are not created per se, just assembled, this
1317 a2cfdea2 Iustin Pop
    function only initializes the metadata.
1318 a2cfdea2 Iustin Pop

1319 a2cfdea2 Iustin Pop
    """
1320 a2cfdea2 Iustin Pop
    if len(children) != 2:
1321 a2cfdea2 Iustin Pop
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1322 a2cfdea2 Iustin Pop
    meta = children[1]
1323 a2cfdea2 Iustin Pop
    meta.Assemble()
1324 a2cfdea2 Iustin Pop
    if not meta.Attach():
1325 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Can't attach to meta device")
1326 a2cfdea2 Iustin Pop
    if not cls._CheckMetaSize(meta.dev_path):
1327 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Invalid meta device size")
1328 a2cfdea2 Iustin Pop
    cls._InitMeta(cls._FindUnusedMinor(), meta.dev_path)
1329 a2cfdea2 Iustin Pop
    if not cls._IsValidMeta(meta.dev_path):
1330 a2cfdea2 Iustin Pop
      raise errors.BlockDeviceError("Cannot initalize meta device")
1331 a2cfdea2 Iustin Pop
    return cls(unique_id, children)
1332 a2cfdea2 Iustin Pop
1333 a8083063 Iustin Pop
1334 6f695a2e Manuel Franceschini
class FileStorage(BlockDev):
1335 6f695a2e Manuel Franceschini
  """File device.
1336 abdf0113 Iustin Pop

1337 6f695a2e Manuel Franceschini
  This class represents the a file storage backend device.
1338 6f695a2e Manuel Franceschini

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

1341 6f695a2e Manuel Franceschini
  """
1342 6f695a2e Manuel Franceschini
  def __init__(self, unique_id, children):
1343 6f695a2e Manuel Franceschini
    """Initalizes a file device backend.
1344 6f695a2e Manuel Franceschini

1345 6f695a2e Manuel Franceschini
    """
1346 6f695a2e Manuel Franceschini
    if children:
1347 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("Invalid setup for file device")
1348 6f695a2e Manuel Franceschini
    super(FileStorage, self).__init__(unique_id, children)
1349 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1350 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1351 6f695a2e Manuel Franceschini
    self.driver = unique_id[0]
1352 6f695a2e Manuel Franceschini
    self.dev_path = unique_id[1]
1353 6f695a2e Manuel Franceschini
1354 6f695a2e Manuel Franceschini
  def Assemble(self):
1355 6f695a2e Manuel Franceschini
    """Assemble the device.
1356 6f695a2e Manuel Franceschini

1357 6f695a2e Manuel Franceschini
    Checks whether the file device exists, raises BlockDeviceError otherwise.
1358 6f695a2e Manuel Franceschini

1359 6f695a2e Manuel Franceschini
    """
1360 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1361 6f695a2e Manuel Franceschini
      raise errors.BlockDeviceError("File device '%s' does not exist." %
1362 6f695a2e Manuel Franceschini
                                    self.dev_path)
1363 6f695a2e Manuel Franceschini
    return True
1364 6f695a2e Manuel Franceschini
1365 6f695a2e Manuel Franceschini
  def Shutdown(self):
1366 6f695a2e Manuel Franceschini
    """Shutdown the device.
1367 6f695a2e Manuel Franceschini

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

1371 6f695a2e Manuel Franceschini
    """
1372 6f695a2e Manuel Franceschini
    return True
1373 6f695a2e Manuel Franceschini
1374 6f695a2e Manuel Franceschini
  def Open(self, force=False):
1375 6f695a2e Manuel Franceschini
    """Make the device ready for I/O.
1376 6f695a2e Manuel Franceschini

1377 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1378 6f695a2e Manuel Franceschini

1379 6f695a2e Manuel Franceschini
    """
1380 6f695a2e Manuel Franceschini
    pass
1381 6f695a2e Manuel Franceschini
1382 6f695a2e Manuel Franceschini
  def Close(self):
1383 6f695a2e Manuel Franceschini
    """Notifies that the device will no longer be used for I/O.
1384 6f695a2e Manuel Franceschini

1385 6f695a2e Manuel Franceschini
    This is a no-op for the file type.
1386 6f695a2e Manuel Franceschini

1387 6f695a2e Manuel Franceschini
    """
1388 6f695a2e Manuel Franceschini
    pass
1389 6f695a2e Manuel Franceschini
1390 6f695a2e Manuel Franceschini
  def Remove(self):
1391 6f695a2e Manuel Franceschini
    """Remove the file backing the block device.
1392 6f695a2e Manuel Franceschini

1393 6f695a2e Manuel Franceschini
    Returns:
1394 6f695a2e Manuel Franceschini
      boolean indicating wheter removal of file was successful or not.
1395 6f695a2e Manuel Franceschini

1396 6f695a2e Manuel Franceschini
    """
1397 6f695a2e Manuel Franceschini
    if not os.path.exists(self.dev_path):
1398 6f695a2e Manuel Franceschini
      return True
1399 6f695a2e Manuel Franceschini
    try:
1400 6f695a2e Manuel Franceschini
      os.remove(self.dev_path)
1401 6f695a2e Manuel Franceschini
      return True
1402 6f695a2e Manuel Franceschini
    except OSError, err:
1403 6f695a2e Manuel Franceschini
      logger.Error("Can't remove file '%s': %s"
1404 6f695a2e Manuel Franceschini
                   % (self.dev_path, err))
1405 6f695a2e Manuel Franceschini
      return False
1406 6f695a2e Manuel Franceschini
1407 6f695a2e Manuel Franceschini
  def Attach(self):
1408 6f695a2e Manuel Franceschini
    """Attach to an existing file.
1409 6f695a2e Manuel Franceschini

1410 6f695a2e Manuel Franceschini
    Check if this file already exists.
1411 6f695a2e Manuel Franceschini

1412 6f695a2e Manuel Franceschini
    Returns:
1413 6f695a2e Manuel Franceschini
      boolean indicating if file exists or not.
1414 6f695a2e Manuel Franceschini

1415 6f695a2e Manuel Franceschini
    """
1416 6f695a2e Manuel Franceschini
    if os.path.exists(self.dev_path):
1417 6f695a2e Manuel Franceschini
      return True
1418 6f695a2e Manuel Franceschini
    return False
1419 6f695a2e Manuel Franceschini
1420 6f695a2e Manuel Franceschini
  @classmethod
1421 6f695a2e Manuel Franceschini
  def Create(cls, unique_id, children, size):
1422 6f695a2e Manuel Franceschini
    """Create a new file.
1423 6f695a2e Manuel Franceschini

1424 6f695a2e Manuel Franceschini
    Args:
1425 6f695a2e Manuel Franceschini
      children:
1426 6f695a2e Manuel Franceschini
      size: integer size of file in MiB
1427 6f695a2e Manuel Franceschini

1428 6f695a2e Manuel Franceschini
    Returns:
1429 6f695a2e Manuel Franceschini
      A ganeti.bdev.FileStorage object.
1430 6f695a2e Manuel Franceschini

1431 6f695a2e Manuel Franceschini
    """
1432 6f695a2e Manuel Franceschini
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 2:
1433 6f695a2e Manuel Franceschini
      raise ValueError("Invalid configuration data %s" % str(unique_id))
1434 6f695a2e Manuel Franceschini
    dev_path = unique_id[1]
1435 6f695a2e Manuel Franceschini
    try:
1436 6f695a2e Manuel Franceschini
      f = open(dev_path, 'w')
1437 6f695a2e Manuel Franceschini
    except IOError, err:
1438 b62ddbe5 Guido Trotter
      raise errors.BlockDeviceError("Could not create '%'" % err)
1439 6f695a2e Manuel Franceschini
    else:
1440 6f695a2e Manuel Franceschini
      f.truncate(size * 1024 * 1024)
1441 6f695a2e Manuel Franceschini
      f.close()
1442 6f695a2e Manuel Franceschini
1443 6f695a2e Manuel Franceschini
    return FileStorage(unique_id, children)
1444 6f695a2e Manuel Franceschini
1445 6f695a2e Manuel Franceschini
1446 a8083063 Iustin Pop
DEV_MAP = {
1447 fe96220b Iustin Pop
  constants.LD_LV: LogicalVolume,
1448 a1f445d3 Iustin Pop
  constants.LD_DRBD8: DRBD8,
1449 6f695a2e Manuel Franceschini
  constants.LD_FILE: FileStorage,
1450 a8083063 Iustin Pop
  }
1451 a8083063 Iustin Pop
1452 a8083063 Iustin Pop
1453 a8083063 Iustin Pop
def FindDevice(dev_type, unique_id, children):
1454 a8083063 Iustin Pop
  """Search for an existing, assembled device.
1455 a8083063 Iustin Pop

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

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

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

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

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