Statistics
| Branch: | Tag: | Revision:

root / lib / storage / drbd.py @ 688b5752

History | View | Annotate | Download (34.7 kB)

1 89ff748d Thomas Thrainer
#
2 89ff748d Thomas Thrainer
#
3 89ff748d Thomas Thrainer
4 89ff748d Thomas Thrainer
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 Google Inc.
5 89ff748d Thomas Thrainer
#
6 89ff748d Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 89ff748d Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 89ff748d Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 89ff748d Thomas Thrainer
# (at your option) any later version.
10 89ff748d Thomas Thrainer
#
11 89ff748d Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 89ff748d Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 89ff748d Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 89ff748d Thomas Thrainer
# General Public License for more details.
15 89ff748d Thomas Thrainer
#
16 89ff748d Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 89ff748d Thomas Thrainer
# along with this program; if not, write to the Free Software
18 89ff748d Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 89ff748d Thomas Thrainer
# 02110-1301, USA.
20 89ff748d Thomas Thrainer
21 89ff748d Thomas Thrainer
22 89ff748d Thomas Thrainer
"""DRBD block device related functionality"""
23 89ff748d Thomas Thrainer
24 89ff748d Thomas Thrainer
import errno
25 89ff748d Thomas Thrainer
import logging
26 89ff748d Thomas Thrainer
import time
27 89ff748d Thomas Thrainer
28 89ff748d Thomas Thrainer
from ganeti import constants
29 89ff748d Thomas Thrainer
from ganeti import utils
30 89ff748d Thomas Thrainer
from ganeti import errors
31 89ff748d Thomas Thrainer
from ganeti import netutils
32 89ff748d Thomas Thrainer
from ganeti import objects
33 cde49218 Helga Velroyen
from ganeti.storage import base
34 cde49218 Helga Velroyen
from ganeti.storage.drbd_info import DRBD8Info
35 cde49218 Helga Velroyen
from ganeti.storage import drbd_info
36 cde49218 Helga Velroyen
from ganeti.storage import drbd_cmdgen
37 89ff748d Thomas Thrainer
38 89ff748d Thomas Thrainer
39 89ff748d Thomas Thrainer
# Size of reads in _CanReadDevice
40 89ff748d Thomas Thrainer
41 89ff748d Thomas Thrainer
_DEVICE_READ_SIZE = 128 * 1024
42 89ff748d Thomas Thrainer
43 89ff748d Thomas Thrainer
44 47e0abee Thomas Thrainer
class DRBD8(object):
45 47e0abee Thomas Thrainer
  """Various methods to deals with the DRBD system as a whole.
46 47e0abee Thomas Thrainer

47 47e0abee Thomas Thrainer
  This class provides a set of methods to deal with the DRBD installation on
48 47e0abee Thomas Thrainer
  the node or with uninitialized devices as opposed to a DRBD device.
49 47e0abee Thomas Thrainer

50 47e0abee Thomas Thrainer
  """
51 47e0abee Thomas Thrainer
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
52 47e0abee Thomas Thrainer
53 47e0abee Thomas Thrainer
  _MAX_MINORS = 255
54 47e0abee Thomas Thrainer
55 47e0abee Thomas Thrainer
  @staticmethod
56 47e0abee Thomas Thrainer
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
57 47e0abee Thomas Thrainer
    """Returns DRBD usermode_helper currently set.
58 47e0abee Thomas Thrainer

59 47e0abee Thomas Thrainer
    @type filename: string
60 47e0abee Thomas Thrainer
    @param filename: the filename to read the usermode helper from
61 47e0abee Thomas Thrainer
    @rtype: string
62 47e0abee Thomas Thrainer
    @return: the currently configured DRBD usermode helper
63 47e0abee Thomas Thrainer

64 47e0abee Thomas Thrainer
    """
65 47e0abee Thomas Thrainer
    try:
66 47e0abee Thomas Thrainer
      helper = utils.ReadFile(filename).splitlines()[0]
67 47e0abee Thomas Thrainer
    except EnvironmentError, err:
68 47e0abee Thomas Thrainer
      if err.errno == errno.ENOENT:
69 47e0abee Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
70 47e0abee Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
71 47e0abee Thomas Thrainer
      else:
72 47e0abee Thomas Thrainer
        base.ThrowError("Can't read DRBD helper file %s: %s",
73 47e0abee Thomas Thrainer
                        filename, str(err))
74 47e0abee Thomas Thrainer
    if not helper:
75 47e0abee Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
76 47e0abee Thomas Thrainer
    return helper
77 47e0abee Thomas Thrainer
78 47e0abee Thomas Thrainer
  @staticmethod
79 47e0abee Thomas Thrainer
  def GetProcInfo():
80 47e0abee Thomas Thrainer
    """Reads and parses information from /proc/drbd.
81 47e0abee Thomas Thrainer

82 47e0abee Thomas Thrainer
    @rtype: DRBD8Info
83 47e0abee Thomas Thrainer
    @return: a L{DRBD8Info} instance containing the current /proc/drbd info
84 47e0abee Thomas Thrainer

85 47e0abee Thomas Thrainer
    """
86 47e0abee Thomas Thrainer
    return DRBD8Info.CreateFromFile()
87 47e0abee Thomas Thrainer
88 47e0abee Thomas Thrainer
  @staticmethod
89 47e0abee Thomas Thrainer
  def GetUsedDevs():
90 47e0abee Thomas Thrainer
    """Compute the list of used DRBD minors.
91 47e0abee Thomas Thrainer

92 47e0abee Thomas Thrainer
    @rtype: list of ints
93 47e0abee Thomas Thrainer

94 47e0abee Thomas Thrainer
    """
95 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
96 47e0abee Thomas Thrainer
    return filter(lambda m: not info.GetMinorStatus(m).is_unconfigured,
97 47e0abee Thomas Thrainer
                  info.GetMinors())
98 47e0abee Thomas Thrainer
99 47e0abee Thomas Thrainer
  @staticmethod
100 47e0abee Thomas Thrainer
  def FindUnusedMinor():
101 47e0abee Thomas Thrainer
    """Find an unused DRBD device.
102 47e0abee Thomas Thrainer

103 47e0abee Thomas Thrainer
    This is specific to 8.x as the minors are allocated dynamically,
104 47e0abee Thomas Thrainer
    so non-existing numbers up to a max minor count are actually free.
105 47e0abee Thomas Thrainer

106 47e0abee Thomas Thrainer
    @rtype: int
107 47e0abee Thomas Thrainer

108 47e0abee Thomas Thrainer
    """
109 47e0abee Thomas Thrainer
    highest = None
110 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
111 47e0abee Thomas Thrainer
    for minor in info.GetMinors():
112 47e0abee Thomas Thrainer
      status = info.GetMinorStatus(minor)
113 47e0abee Thomas Thrainer
      if not status.is_in_use:
114 47e0abee Thomas Thrainer
        return minor
115 47e0abee Thomas Thrainer
      highest = max(highest, minor)
116 47e0abee Thomas Thrainer
117 47e0abee Thomas Thrainer
    if highest is None: # there are no minors in use at all
118 47e0abee Thomas Thrainer
      return 0
119 47e0abee Thomas Thrainer
    if highest >= DRBD8._MAX_MINORS:
120 47e0abee Thomas Thrainer
      logging.error("Error: no free drbd minors!")
121 47e0abee Thomas Thrainer
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
122 47e0abee Thomas Thrainer
123 47e0abee Thomas Thrainer
    return highest + 1
124 47e0abee Thomas Thrainer
125 47e0abee Thomas Thrainer
  @staticmethod
126 47e0abee Thomas Thrainer
  def GetCmdGenerator(info):
127 47e0abee Thomas Thrainer
    """Creates a suitable L{BaseDRBDCmdGenerator} based on the given info.
128 47e0abee Thomas Thrainer

129 47e0abee Thomas Thrainer
    @type info: DRBD8Info
130 47e0abee Thomas Thrainer
    @rtype: BaseDRBDCmdGenerator
131 47e0abee Thomas Thrainer

132 47e0abee Thomas Thrainer
    """
133 47e0abee Thomas Thrainer
    version = info.GetVersion()
134 47e0abee Thomas Thrainer
    if version["k_minor"] <= 3:
135 47e0abee Thomas Thrainer
      return drbd_cmdgen.DRBD83CmdGenerator(version)
136 47e0abee Thomas Thrainer
    else:
137 47e0abee Thomas Thrainer
      return drbd_cmdgen.DRBD84CmdGenerator(version)
138 47e0abee Thomas Thrainer
139 47e0abee Thomas Thrainer
  @staticmethod
140 47e0abee Thomas Thrainer
  def ShutdownAll(minor):
141 47e0abee Thomas Thrainer
    """Deactivate the device.
142 47e0abee Thomas Thrainer

143 47e0abee Thomas Thrainer
    This will, of course, fail if the device is in use.
144 47e0abee Thomas Thrainer

145 47e0abee Thomas Thrainer
    @type minor: int
146 47e0abee Thomas Thrainer
    @param minor: the minor to shut down
147 47e0abee Thomas Thrainer

148 47e0abee Thomas Thrainer
    """
149 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
150 47e0abee Thomas Thrainer
    cmd_gen = DRBD8.GetCmdGenerator(info)
151 47e0abee Thomas Thrainer
152 47e0abee Thomas Thrainer
    cmd = cmd_gen.GenDownCmd(minor)
153 47e0abee Thomas Thrainer
    result = utils.RunCmd(cmd)
154 47e0abee Thomas Thrainer
    if result.failed:
155 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown drbd device: %s",
156 47e0abee Thomas Thrainer
                      minor, result.output)
157 47e0abee Thomas Thrainer
158 47e0abee Thomas Thrainer
159 239364d0 Thomas Thrainer
class DRBD8Dev(base.BlockDev):
160 fd300bc7 Thomas Thrainer
  """DRBD v8.x block device.
161 fd300bc7 Thomas Thrainer

162 fd300bc7 Thomas Thrainer
  This implements the local host part of the DRBD device, i.e. it
163 fd300bc7 Thomas Thrainer
  doesn't do anything to the supposed peer. If you need a fully
164 fd300bc7 Thomas Thrainer
  connected DRBD pair, you need to use this class on both hosts.
165 fd300bc7 Thomas Thrainer

166 fd300bc7 Thomas Thrainer
  The unique_id for the drbd device is a (local_ip, local_port,
167 fd300bc7 Thomas Thrainer
  remote_ip, remote_port, local_minor, secret) tuple, and it must have
168 fd300bc7 Thomas Thrainer
  two children: the data device and the meta_device. The meta device
169 fd300bc7 Thomas Thrainer
  is checked for valid size and is zeroed on create.
170 89ff748d Thomas Thrainer

171 89ff748d Thomas Thrainer
  """
172 89ff748d Thomas Thrainer
  _DRBD_MAJOR = 147
173 89ff748d Thomas Thrainer
174 fd300bc7 Thomas Thrainer
  # timeout constants
175 fd300bc7 Thomas Thrainer
  _NET_RECONFIG_TIMEOUT = 60
176 fd300bc7 Thomas Thrainer
177 fd300bc7 Thomas Thrainer
  def __init__(self, unique_id, children, size, params):
178 fd300bc7 Thomas Thrainer
    if children and children.count(None) > 0:
179 fd300bc7 Thomas Thrainer
      children = []
180 fd300bc7 Thomas Thrainer
    if len(children) not in (0, 2):
181 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(children))
182 fd300bc7 Thomas Thrainer
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
183 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(unique_id))
184 fd300bc7 Thomas Thrainer
    (self._lhost, self._lport,
185 fd300bc7 Thomas Thrainer
     self._rhost, self._rport,
186 fd300bc7 Thomas Thrainer
     self._aminor, self._secret) = unique_id
187 fd300bc7 Thomas Thrainer
    if children:
188 fd300bc7 Thomas Thrainer
      if not _CanReadDevice(children[1].dev_path):
189 fd300bc7 Thomas Thrainer
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
190 fd300bc7 Thomas Thrainer
        children = []
191 239364d0 Thomas Thrainer
    super(DRBD8Dev, self).__init__(unique_id, children, size, params)
192 fd300bc7 Thomas Thrainer
    self.major = self._DRBD_MAJOR
193 2fe690f1 Thomas Thrainer
194 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
195 47e0abee Thomas Thrainer
    version = info.GetVersion()
196 fd300bc7 Thomas Thrainer
    if version["k_major"] != 8:
197 fd300bc7 Thomas Thrainer
      base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
198 fd300bc7 Thomas Thrainer
                      " usage: kernel is %s.%s, ganeti wants 8.x",
199 fd300bc7 Thomas Thrainer
                      version["k_major"], version["k_minor"])
200 fd300bc7 Thomas Thrainer
201 27c7d9c3 Thomas Thrainer
    if version["k_minor"] <= 3:
202 47e0abee Thomas Thrainer
      self._show_info_cls = drbd_info.DRBD83ShowInfo
203 27c7d9c3 Thomas Thrainer
    else:
204 47e0abee Thomas Thrainer
      self._show_info_cls = drbd_info.DRBD84ShowInfo
205 daec28a7 Thomas Thrainer
206 47e0abee Thomas Thrainer
    self._cmd_gen = DRBD8.GetCmdGenerator(info)
207 27c7d9c3 Thomas Thrainer
208 fd300bc7 Thomas Thrainer
    if (self._lhost is not None and self._lhost == self._rhost and
209 fd300bc7 Thomas Thrainer
            self._lport == self._rport):
210 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data, same local/remote %s" %
211 fd300bc7 Thomas Thrainer
                       (unique_id,))
212 fd300bc7 Thomas Thrainer
    self.Attach()
213 fd300bc7 Thomas Thrainer
214 89ff748d Thomas Thrainer
  @staticmethod
215 89ff748d Thomas Thrainer
  def _DevPath(minor):
216 89ff748d Thomas Thrainer
    """Return the path to a drbd device for a given minor.
217 89ff748d Thomas Thrainer

218 47e0abee Thomas Thrainer
    @type minor: int
219 47e0abee Thomas Thrainer
    @rtype: string
220 89ff748d Thomas Thrainer

221 89ff748d Thomas Thrainer
    """
222 47e0abee Thomas Thrainer
    return "/dev/drbd%d" % minor
223 89ff748d Thomas Thrainer
224 89ff748d Thomas Thrainer
  def _SetFromMinor(self, minor):
225 89ff748d Thomas Thrainer
    """Set our parameters based on the given minor.
226 89ff748d Thomas Thrainer

227 89ff748d Thomas Thrainer
    This sets our minor variable and our dev_path.
228 89ff748d Thomas Thrainer

229 47e0abee Thomas Thrainer
    @type minor: int
230 47e0abee Thomas Thrainer

231 89ff748d Thomas Thrainer
    """
232 89ff748d Thomas Thrainer
    if minor is None:
233 89ff748d Thomas Thrainer
      self.minor = self.dev_path = None
234 89ff748d Thomas Thrainer
      self.attached = False
235 89ff748d Thomas Thrainer
    else:
236 89ff748d Thomas Thrainer
      self.minor = minor
237 89ff748d Thomas Thrainer
      self.dev_path = self._DevPath(minor)
238 89ff748d Thomas Thrainer
      self.attached = True
239 89ff748d Thomas Thrainer
240 89ff748d Thomas Thrainer
  @staticmethod
241 89ff748d Thomas Thrainer
  def _CheckMetaSize(meta_device):
242 89ff748d Thomas Thrainer
    """Check if the given meta device looks like a valid one.
243 89ff748d Thomas Thrainer

244 89ff748d Thomas Thrainer
    This currently only checks the size, which must be around
245 89ff748d Thomas Thrainer
    128MiB.
246 89ff748d Thomas Thrainer

247 47e0abee Thomas Thrainer
    @type meta_device: string
248 47e0abee Thomas Thrainer
    @param meta_device: the path to the device to check
249 47e0abee Thomas Thrainer

250 89ff748d Thomas Thrainer
    """
251 89ff748d Thomas Thrainer
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
252 89ff748d Thomas Thrainer
    if result.failed:
253 89ff748d Thomas Thrainer
      base.ThrowError("Failed to get device size: %s - %s",
254 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
255 89ff748d Thomas Thrainer
    try:
256 89ff748d Thomas Thrainer
      sectors = int(result.stdout)
257 89ff748d Thomas Thrainer
    except (TypeError, ValueError):
258 89ff748d Thomas Thrainer
      base.ThrowError("Invalid output from blockdev: '%s'", result.stdout)
259 89ff748d Thomas Thrainer
    num_bytes = sectors * 512
260 89ff748d Thomas Thrainer
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
261 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too small (%.2fMib)",
262 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
263 89ff748d Thomas Thrainer
    # the maximum *valid* size of the meta device when living on top
264 89ff748d Thomas Thrainer
    # of LVM is hard to compute: it depends on the number of stripes
265 89ff748d Thomas Thrainer
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
266 89ff748d Thomas Thrainer
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
267 89ff748d Thomas Thrainer
    # size meta device; as such, we restrict it to 1GB (a little bit
268 89ff748d Thomas Thrainer
    # too generous, but making assumptions about PE size is hard)
269 89ff748d Thomas Thrainer
    if num_bytes > 1024 * 1024 * 1024:
270 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too big (%.2fMiB)",
271 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
272 89ff748d Thomas Thrainer
273 09a78e1c Thomas Thrainer
  def _GetShowData(self, minor):
274 47e0abee Thomas Thrainer
    """Return the `drbdsetup show` data.
275 47e0abee Thomas Thrainer

276 47e0abee Thomas Thrainer
    @type minor: int
277 47e0abee Thomas Thrainer
    @param minor: the minor to collect show output for
278 47e0abee Thomas Thrainer
    @rtype: string
279 89ff748d Thomas Thrainer

280 89ff748d Thomas Thrainer
    """
281 09a78e1c Thomas Thrainer
    result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
282 89ff748d Thomas Thrainer
    if result.failed:
283 89ff748d Thomas Thrainer
      logging.error("Can't display the drbd config: %s - %s",
284 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
285 89ff748d Thomas Thrainer
      return None
286 89ff748d Thomas Thrainer
    return result.stdout
287 89ff748d Thomas Thrainer
288 27c7d9c3 Thomas Thrainer
  def _GetShowInfo(self, minor):
289 47e0abee Thomas Thrainer
    """Return parsed information from `drbdsetup show`.
290 47e0abee Thomas Thrainer

291 47e0abee Thomas Thrainer
    @type minor: int
292 47e0abee Thomas Thrainer
    @param minor: the minor to return information for
293 47e0abee Thomas Thrainer
    @rtype: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
294 47e0abee Thomas Thrainer

295 47e0abee Thomas Thrainer
    """
296 27c7d9c3 Thomas Thrainer
    return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
297 27c7d9c3 Thomas Thrainer
298 89ff748d Thomas Thrainer
  def _MatchesLocal(self, info):
299 89ff748d Thomas Thrainer
    """Test if our local config matches with an existing device.
300 89ff748d Thomas Thrainer

301 47e0abee Thomas Thrainer
    The parameter should be as returned from `_GetShowInfo()`. This
302 89ff748d Thomas Thrainer
    method tests if our local backing device is the same as the one in
303 89ff748d Thomas Thrainer
    the info parameter, in effect testing if we look like the given
304 89ff748d Thomas Thrainer
    device.
305 89ff748d Thomas Thrainer

306 47e0abee Thomas Thrainer
    @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
307 47e0abee Thomas Thrainer
    @rtype: boolean
308 47e0abee Thomas Thrainer

309 89ff748d Thomas Thrainer
    """
310 89ff748d Thomas Thrainer
    if self._children:
311 89ff748d Thomas Thrainer
      backend, meta = self._children
312 89ff748d Thomas Thrainer
    else:
313 89ff748d Thomas Thrainer
      backend = meta = None
314 89ff748d Thomas Thrainer
315 89ff748d Thomas Thrainer
    if backend is not None:
316 89ff748d Thomas Thrainer
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
317 89ff748d Thomas Thrainer
    else:
318 89ff748d Thomas Thrainer
      retval = ("local_dev" not in info)
319 89ff748d Thomas Thrainer
320 89ff748d Thomas Thrainer
    if meta is not None:
321 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" in info and
322 89ff748d Thomas Thrainer
                           info["meta_dev"] == meta.dev_path)
323 085b8e24 Thomas Thrainer
      if "meta_index" in info:
324 085b8e24 Thomas Thrainer
        retval = retval and info["meta_index"] == 0
325 89ff748d Thomas Thrainer
    else:
326 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" not in info and
327 89ff748d Thomas Thrainer
                           "meta_index" not in info)
328 89ff748d Thomas Thrainer
    return retval
329 89ff748d Thomas Thrainer
330 89ff748d Thomas Thrainer
  def _MatchesNet(self, info):
331 89ff748d Thomas Thrainer
    """Test if our network config matches with an existing device.
332 89ff748d Thomas Thrainer

333 47e0abee Thomas Thrainer
    The parameter should be as returned from `_GetShowInfo()`. This
334 89ff748d Thomas Thrainer
    method tests if our network configuration is the same as the one
335 89ff748d Thomas Thrainer
    in the info parameter, in effect testing if we look like the given
336 89ff748d Thomas Thrainer
    device.
337 89ff748d Thomas Thrainer

338 47e0abee Thomas Thrainer
    @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
339 47e0abee Thomas Thrainer
    @rtype: boolean
340 47e0abee Thomas Thrainer

341 89ff748d Thomas Thrainer
    """
342 89ff748d Thomas Thrainer
    if (((self._lhost is None and not ("local_addr" in info)) and
343 89ff748d Thomas Thrainer
         (self._rhost is None and not ("remote_addr" in info)))):
344 89ff748d Thomas Thrainer
      return True
345 89ff748d Thomas Thrainer
346 89ff748d Thomas Thrainer
    if self._lhost is None:
347 89ff748d Thomas Thrainer
      return False
348 89ff748d Thomas Thrainer
349 89ff748d Thomas Thrainer
    if not ("local_addr" in info and
350 89ff748d Thomas Thrainer
            "remote_addr" in info):
351 89ff748d Thomas Thrainer
      return False
352 89ff748d Thomas Thrainer
353 89ff748d Thomas Thrainer
    retval = (info["local_addr"] == (self._lhost, self._lport))
354 89ff748d Thomas Thrainer
    retval = (retval and
355 89ff748d Thomas Thrainer
              info["remote_addr"] == (self._rhost, self._rport))
356 89ff748d Thomas Thrainer
    return retval
357 89ff748d Thomas Thrainer
358 89ff748d Thomas Thrainer
  def _AssembleLocal(self, minor, backend, meta, size):
359 89ff748d Thomas Thrainer
    """Configure the local part of a DRBD device.
360 89ff748d Thomas Thrainer

361 47e0abee Thomas Thrainer
    @type minor: int
362 47e0abee Thomas Thrainer
    @param minor: the minor to assemble locally
363 47e0abee Thomas Thrainer
    @type backend: string
364 47e0abee Thomas Thrainer
    @param backend: path to the data device to use
365 47e0abee Thomas Thrainer
    @type meta: string
366 47e0abee Thomas Thrainer
    @param meta: path to the meta device to use
367 47e0abee Thomas Thrainer
    @type size: int
368 47e0abee Thomas Thrainer
    @param size: size in MiB
369 47e0abee Thomas Thrainer

370 89ff748d Thomas Thrainer
    """
371 09a78e1c Thomas Thrainer
    cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta,
372 09a78e1c Thomas Thrainer
                                          size, self.params)
373 89ff748d Thomas Thrainer
374 09a78e1c Thomas Thrainer
    for cmd in cmds:
375 09a78e1c Thomas Thrainer
      result = utils.RunCmd(cmd)
376 09a78e1c Thomas Thrainer
      if result.failed:
377 09a78e1c Thomas Thrainer
        base.ThrowError("drbd%d: can't attach local disk: %s",
378 09a78e1c Thomas Thrainer
                        minor, result.output)
379 89ff748d Thomas Thrainer
380 89ff748d Thomas Thrainer
  def _AssembleNet(self, minor, net_info, protocol,
381 89ff748d Thomas Thrainer
                   dual_pri=False, hmac=None, secret=None):
382 89ff748d Thomas Thrainer
    """Configure the network part of the device.
383 89ff748d Thomas Thrainer

384 47e0abee Thomas Thrainer
    @type minor: int
385 47e0abee Thomas Thrainer
    @param minor: the minor to assemble the network for
386 47e0abee Thomas Thrainer
    @type net_info: (string, int, string, int)
387 47e0abee Thomas Thrainer
    @param net_info: tuple containing the local address, local port, remote
388 47e0abee Thomas Thrainer
      address and remote port
389 47e0abee Thomas Thrainer
    @type protocol: string
390 47e0abee Thomas Thrainer
    @param protocol: either "ipv4" or "ipv6"
391 47e0abee Thomas Thrainer
    @type dual_pri: boolean
392 47e0abee Thomas Thrainer
    @param dual_pri: whether two primaries should be allowed or not
393 47e0abee Thomas Thrainer
    @type hmac: string
394 47e0abee Thomas Thrainer
    @param hmac: the HMAC algorithm to use
395 47e0abee Thomas Thrainer
    @type secret: string
396 47e0abee Thomas Thrainer
    @param secret: the shared secret to use
397 47e0abee Thomas Thrainer

398 89ff748d Thomas Thrainer
    """
399 89ff748d Thomas Thrainer
    lhost, lport, rhost, rport = net_info
400 89ff748d Thomas Thrainer
    if None in net_info:
401 89ff748d Thomas Thrainer
      # we don't want network connection and actually want to make
402 89ff748d Thomas Thrainer
      # sure its shutdown
403 89ff748d Thomas Thrainer
      self._ShutdownNet(minor)
404 89ff748d Thomas Thrainer
      return
405 89ff748d Thomas Thrainer
406 89ff748d Thomas Thrainer
    # Workaround for a race condition. When DRBD is doing its dance to
407 89ff748d Thomas Thrainer
    # establish a connection with its peer, it also sends the
408 89ff748d Thomas Thrainer
    # synchronization speed over the wire. In some cases setting the
409 89ff748d Thomas Thrainer
    # sync speed only after setting up both sides can race with DRBD
410 89ff748d Thomas Thrainer
    # connecting, hence we set it here before telling DRBD anything
411 89ff748d Thomas Thrainer
    # about its peer.
412 89ff748d Thomas Thrainer
    sync_errors = self._SetMinorSyncParams(minor, self.params)
413 89ff748d Thomas Thrainer
    if sync_errors:
414 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
415 89ff748d Thomas Thrainer
                      (minor, utils.CommaJoin(sync_errors)))
416 89ff748d Thomas Thrainer
417 daec28a7 Thomas Thrainer
    family = self._GetNetFamily(minor, lhost, rhost)
418 89ff748d Thomas Thrainer
419 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
420 09a78e1c Thomas Thrainer
                                      rhost, rport, protocol,
421 09a78e1c Thomas Thrainer
                                      dual_pri, hmac, secret, self.params)
422 09a78e1c Thomas Thrainer
423 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
424 89ff748d Thomas Thrainer
    if result.failed:
425 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't setup network: %s - %s",
426 89ff748d Thomas Thrainer
                      minor, result.fail_reason, result.output)
427 89ff748d Thomas Thrainer
428 89ff748d Thomas Thrainer
    def _CheckNetworkConfig():
429 27c7d9c3 Thomas Thrainer
      info = self._GetShowInfo(minor)
430 89ff748d Thomas Thrainer
      if not "local_addr" in info or not "remote_addr" in info:
431 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
432 89ff748d Thomas Thrainer
433 89ff748d Thomas Thrainer
      if (info["local_addr"] != (lhost, lport) or
434 89ff748d Thomas Thrainer
          info["remote_addr"] != (rhost, rport)):
435 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
436 89ff748d Thomas Thrainer
437 89ff748d Thomas Thrainer
    try:
438 89ff748d Thomas Thrainer
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
439 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
440 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: timeout while configuring network", minor)
441 89ff748d Thomas Thrainer
442 daec28a7 Thomas Thrainer
  @staticmethod
443 daec28a7 Thomas Thrainer
  def _GetNetFamily(minor, lhost, rhost):
444 daec28a7 Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
445 daec28a7 Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
446 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
447 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
448 daec28a7 Thomas Thrainer
      return "ipv6"
449 daec28a7 Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
450 daec28a7 Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
451 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
452 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
453 daec28a7 Thomas Thrainer
      return "ipv4"
454 daec28a7 Thomas Thrainer
    else:
455 daec28a7 Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
456 daec28a7 Thomas Thrainer
457 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
458 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
459 89ff748d Thomas Thrainer

460 47e0abee Thomas Thrainer
    @type devices: list of L{BlockDev}
461 47e0abee Thomas Thrainer
    @param devices: a list of exactly two L{BlockDev} objects; the first
462 47e0abee Thomas Thrainer
      denotes the data device, the second the meta device for this DRBD device
463 47e0abee Thomas Thrainer

464 89ff748d Thomas Thrainer
    """
465 89ff748d Thomas Thrainer
    if self.minor is None:
466 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
467 89ff748d Thomas Thrainer
                      self._aminor)
468 89ff748d Thomas Thrainer
    if len(devices) != 2:
469 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
470 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
471 89ff748d Thomas Thrainer
    if "local_dev" in info:
472 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
473 89ff748d Thomas Thrainer
    backend, meta = devices
474 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
475 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
476 89ff748d Thomas Thrainer
                      self.minor)
477 89ff748d Thomas Thrainer
    backend.Open()
478 89ff748d Thomas Thrainer
    meta.Open()
479 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
480 47e0abee Thomas Thrainer
    self._InitMeta(DRBD8.FindUnusedMinor(), meta.dev_path)
481 89ff748d Thomas Thrainer
482 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
483 89ff748d Thomas Thrainer
    self._children = devices
484 89ff748d Thomas Thrainer
485 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
486 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
487 89ff748d Thomas Thrainer

488 47e0abee Thomas Thrainer
    @type devices: list of L{BlockDev}
489 47e0abee Thomas Thrainer
    @param devices: a list of exactly two L{BlockDev} objects; the first
490 47e0abee Thomas Thrainer
      denotes the data device, the second the meta device for this DRBD device
491 47e0abee Thomas Thrainer

492 89ff748d Thomas Thrainer
    """
493 89ff748d Thomas Thrainer
    if self.minor is None:
494 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
495 89ff748d Thomas Thrainer
                      self._aminor)
496 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
497 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
498 89ff748d Thomas Thrainer
    if "local_dev" not in info:
499 89ff748d Thomas Thrainer
      return
500 89ff748d Thomas Thrainer
    if len(self._children) != 2:
501 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
502 89ff748d Thomas Thrainer
                      self._children)
503 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
504 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
505 89ff748d Thomas Thrainer
      return
506 89ff748d Thomas Thrainer
    if len(devices) != 2:
507 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
508 89ff748d Thomas Thrainer
                      self.minor)
509 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
510 89ff748d Thomas Thrainer
      if dev != child.dev_path:
511 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
512 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
513 89ff748d Thomas Thrainer
514 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
515 89ff748d Thomas Thrainer
    self._children = []
516 89ff748d Thomas Thrainer
517 2fe690f1 Thomas Thrainer
  def _SetMinorSyncParams(self, minor, params):
518 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
519 89ff748d Thomas Thrainer

520 89ff748d Thomas Thrainer
    This is the low-level implementation.
521 89ff748d Thomas Thrainer

522 89ff748d Thomas Thrainer
    @type minor: int
523 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
524 89ff748d Thomas Thrainer
    @type params: dict
525 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
526 89ff748d Thomas Thrainer
    @rtype: list
527 89ff748d Thomas Thrainer
    @return: a list of error messages
528 89ff748d Thomas Thrainer

529 89ff748d Thomas Thrainer
    """
530 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
531 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
532 89ff748d Thomas Thrainer
    if result.failed:
533 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
534 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
535 89ff748d Thomas Thrainer
      logging.error(msg)
536 89ff748d Thomas Thrainer
      return [msg]
537 89ff748d Thomas Thrainer
538 89ff748d Thomas Thrainer
    return []
539 89ff748d Thomas Thrainer
540 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
541 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
542 89ff748d Thomas Thrainer

543 47e0abee Thomas Thrainer
    See L{BlockDev.SetSyncParams} for parameter description.
544 89ff748d Thomas Thrainer

545 89ff748d Thomas Thrainer
    """
546 89ff748d Thomas Thrainer
    if self.minor is None:
547 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
548 89ff748d Thomas Thrainer
      logging.info(err)
549 89ff748d Thomas Thrainer
      return [err]
550 89ff748d Thomas Thrainer
551 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).SetSyncParams(params)
552 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
553 89ff748d Thomas Thrainer
    return children_result
554 89ff748d Thomas Thrainer
555 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
556 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
557 89ff748d Thomas Thrainer

558 47e0abee Thomas Thrainer
    See L{BlockDev.PauseResumeSync} for parameter description.
559 89ff748d Thomas Thrainer

560 89ff748d Thomas Thrainer
    """
561 89ff748d Thomas Thrainer
    if self.minor is None:
562 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
563 89ff748d Thomas Thrainer
      return False
564 89ff748d Thomas Thrainer
565 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).PauseResumeSync(pause)
566 89ff748d Thomas Thrainer
567 89ff748d Thomas Thrainer
    if pause:
568 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
569 89ff748d Thomas Thrainer
    else:
570 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
571 89ff748d Thomas Thrainer
572 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
573 89ff748d Thomas Thrainer
    if result.failed:
574 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
575 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
576 89ff748d Thomas Thrainer
    return not result.failed and children_result
577 89ff748d Thomas Thrainer
578 89ff748d Thomas Thrainer
  def GetProcStatus(self):
579 47e0abee Thomas Thrainer
    """Return the current status data from /proc/drbd for this device.
580 47e0abee Thomas Thrainer

581 47e0abee Thomas Thrainer
    @rtype: DRBD8Status
582 89ff748d Thomas Thrainer

583 89ff748d Thomas Thrainer
    """
584 89ff748d Thomas Thrainer
    if self.minor is None:
585 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
586 89ff748d Thomas Thrainer
                      self._aminor)
587 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
588 47e0abee Thomas Thrainer
    if not info.HasMinorStatus(self.minor):
589 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
590 47e0abee Thomas Thrainer
    return info.GetMinorStatus(self.minor)
591 89ff748d Thomas Thrainer
592 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
593 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
594 89ff748d Thomas Thrainer

595 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
596 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
597 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
598 89ff748d Thomas Thrainer

599 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
600 89ff748d Thomas Thrainer
    network not connected or local disk missing.
601 89ff748d Thomas Thrainer

602 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
603 89ff748d Thomas Thrainer
    disk or not.
604 89ff748d Thomas Thrainer

605 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
606 89ff748d Thomas Thrainer

607 89ff748d Thomas Thrainer
    """
608 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
609 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
610 89ff748d Thomas Thrainer
611 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
612 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
613 89ff748d Thomas Thrainer
614 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
615 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
616 89ff748d Thomas Thrainer
    elif stats.is_diskless:
617 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
618 89ff748d Thomas Thrainer
    else:
619 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
620 89ff748d Thomas Thrainer
621 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
622 89ff748d Thomas Thrainer
                                  major=self.major,
623 89ff748d Thomas Thrainer
                                  minor=self.minor,
624 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
625 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
626 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
627 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
628 89ff748d Thomas Thrainer
629 89ff748d Thomas Thrainer
  def Open(self, force=False):
630 89ff748d Thomas Thrainer
    """Make the local state primary.
631 89ff748d Thomas Thrainer

632 47e0abee Thomas Thrainer
    If the 'force' parameter is given, DRBD is instructed to switch the device
633 47e0abee Thomas Thrainer
    into primary mode. Since this is a potentially dangerous operation, the
634 47e0abee Thomas Thrainer
    force flag should be only given after creation, when it actually is
635 47e0abee Thomas Thrainer
    mandatory.
636 89ff748d Thomas Thrainer

637 89ff748d Thomas Thrainer
    """
638 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
639 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
640 89ff748d Thomas Thrainer
      return False
641 09a78e1c Thomas Thrainer
642 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
643 09a78e1c Thomas Thrainer
644 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
645 89ff748d Thomas Thrainer
    if result.failed:
646 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
647 89ff748d Thomas Thrainer
                      result.output)
648 89ff748d Thomas Thrainer
649 89ff748d Thomas Thrainer
  def Close(self):
650 89ff748d Thomas Thrainer
    """Make the local state secondary.
651 89ff748d Thomas Thrainer

652 89ff748d Thomas Thrainer
    This will, of course, fail if the device is in use.
653 89ff748d Thomas Thrainer

654 89ff748d Thomas Thrainer
    """
655 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
656 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
657 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
658 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
659 89ff748d Thomas Thrainer
    if result.failed:
660 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
661 89ff748d Thomas Thrainer
                      self.minor, result.output)
662 89ff748d Thomas Thrainer
663 89ff748d Thomas Thrainer
  def DisconnectNet(self):
664 89ff748d Thomas Thrainer
    """Removes network configuration.
665 89ff748d Thomas Thrainer

666 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
667 89ff748d Thomas Thrainer

668 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
669 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
670 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
671 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
672 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
673 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
674 89ff748d Thomas Thrainer
    exception.
675 89ff748d Thomas Thrainer

676 89ff748d Thomas Thrainer
    """
677 89ff748d Thomas Thrainer
    if self.minor is None:
678 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: disk not attached in re-attach net",
679 89ff748d Thomas Thrainer
                      self._aminor)
680 89ff748d Thomas Thrainer
681 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
682 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: DRBD disk missing network info in"
683 89ff748d Thomas Thrainer
                      " DisconnectNet()", self.minor)
684 89ff748d Thomas Thrainer
685 89ff748d Thomas Thrainer
    class _DisconnectStatus:
686 89ff748d Thomas Thrainer
      def __init__(self, ever_disconnected):
687 89ff748d Thomas Thrainer
        self.ever_disconnected = ever_disconnected
688 89ff748d Thomas Thrainer
689 89ff748d Thomas Thrainer
    dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
690 89ff748d Thomas Thrainer
691 89ff748d Thomas Thrainer
    def _WaitForDisconnect():
692 89ff748d Thomas Thrainer
      if self.GetProcStatus().is_standalone:
693 89ff748d Thomas Thrainer
        return
694 89ff748d Thomas Thrainer
695 89ff748d Thomas Thrainer
      # retry the disconnect, it seems possible that due to a well-time
696 89ff748d Thomas Thrainer
      # disconnect on the peer, my disconnect command might be ignored and
697 89ff748d Thomas Thrainer
      # forgotten
698 89ff748d Thomas Thrainer
      dstatus.ever_disconnected = \
699 89ff748d Thomas Thrainer
        base.IgnoreError(self._ShutdownNet, self.minor) or \
700 89ff748d Thomas Thrainer
        dstatus.ever_disconnected
701 89ff748d Thomas Thrainer
702 89ff748d Thomas Thrainer
      raise utils.RetryAgain()
703 89ff748d Thomas Thrainer
704 89ff748d Thomas Thrainer
    # Keep start time
705 89ff748d Thomas Thrainer
    start_time = time.time()
706 89ff748d Thomas Thrainer
707 89ff748d Thomas Thrainer
    try:
708 89ff748d Thomas Thrainer
      # Start delay at 100 milliseconds and grow up to 2 seconds
709 89ff748d Thomas Thrainer
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
710 89ff748d Thomas Thrainer
                  self._NET_RECONFIG_TIMEOUT)
711 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
712 89ff748d Thomas Thrainer
      if dstatus.ever_disconnected:
713 89ff748d Thomas Thrainer
        msg = ("drbd%d: device did not react to the"
714 89ff748d Thomas Thrainer
               " 'disconnect' command in a timely manner")
715 89ff748d Thomas Thrainer
      else:
716 89ff748d Thomas Thrainer
        msg = "drbd%d: can't shutdown network, even after multiple retries"
717 89ff748d Thomas Thrainer
718 89ff748d Thomas Thrainer
      base.ThrowError(msg, self.minor)
719 89ff748d Thomas Thrainer
720 89ff748d Thomas Thrainer
    reconfig_time = time.time() - start_time
721 89ff748d Thomas Thrainer
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
722 89ff748d Thomas Thrainer
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
723 89ff748d Thomas Thrainer
                   self.minor, reconfig_time)
724 89ff748d Thomas Thrainer
725 89ff748d Thomas Thrainer
  def AttachNet(self, multimaster):
726 89ff748d Thomas Thrainer
    """Reconnects the network.
727 89ff748d Thomas Thrainer

728 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
729 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
730 89ff748d Thomas Thrainer
    but have valid network configuration data.
731 89ff748d Thomas Thrainer

732 47e0abee Thomas Thrainer
    @type multimaster: boolean
733 47e0abee Thomas Thrainer
    @param multimaster: init the network in dual-primary mode
734 89ff748d Thomas Thrainer

735 89ff748d Thomas Thrainer
    """
736 89ff748d Thomas Thrainer
    if self.minor is None:
737 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
738 89ff748d Thomas Thrainer
739 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
740 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
741 89ff748d Thomas Thrainer
742 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
743 89ff748d Thomas Thrainer
744 89ff748d Thomas Thrainer
    if not status.is_standalone:
745 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
746 89ff748d Thomas Thrainer
                      self.minor)
747 89ff748d Thomas Thrainer
748 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
749 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
750 89ff748d Thomas Thrainer
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
751 89ff748d Thomas Thrainer
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
752 89ff748d Thomas Thrainer
753 89ff748d Thomas Thrainer
  def Attach(self):
754 89ff748d Thomas Thrainer
    """Check if our minor is configured.
755 89ff748d Thomas Thrainer

756 89ff748d Thomas Thrainer
    This doesn't do any device configurations - it only checks if the
757 89ff748d Thomas Thrainer
    minor is in a state different from Unconfigured.
758 89ff748d Thomas Thrainer

759 89ff748d Thomas Thrainer
    Note that this function will not change the state of the system in
760 89ff748d Thomas Thrainer
    any way (except in case of side-effects caused by reading from
761 89ff748d Thomas Thrainer
    /proc).
762 89ff748d Thomas Thrainer

763 89ff748d Thomas Thrainer
    """
764 47e0abee Thomas Thrainer
    used_devs = DRBD8.GetUsedDevs()
765 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
766 89ff748d Thomas Thrainer
      minor = self._aminor
767 89ff748d Thomas Thrainer
    else:
768 89ff748d Thomas Thrainer
      minor = None
769 89ff748d Thomas Thrainer
770 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
771 89ff748d Thomas Thrainer
    return minor is not None
772 89ff748d Thomas Thrainer
773 89ff748d Thomas Thrainer
  def Assemble(self):
774 89ff748d Thomas Thrainer
    """Assemble the drbd.
775 89ff748d Thomas Thrainer

776 89ff748d Thomas Thrainer
    Method:
777 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
778 89ff748d Thomas Thrainer
        our config
779 89ff748d Thomas Thrainer
      - if not, we create it from zero
780 89ff748d Thomas Thrainer
      - anyway, set the device parameters
781 89ff748d Thomas Thrainer

782 89ff748d Thomas Thrainer
    """
783 239364d0 Thomas Thrainer
    super(DRBD8Dev, self).Assemble()
784 89ff748d Thomas Thrainer
785 89ff748d Thomas Thrainer
    self.Attach()
786 89ff748d Thomas Thrainer
    if self.minor is None:
787 89ff748d Thomas Thrainer
      # local device completely unconfigured
788 89ff748d Thomas Thrainer
      self._FastAssemble()
789 89ff748d Thomas Thrainer
    else:
790 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
791 89ff748d Thomas Thrainer
      # the device
792 89ff748d Thomas Thrainer
      self._SlowAssemble()
793 89ff748d Thomas Thrainer
794 89ff748d Thomas Thrainer
    sync_errors = self.SetSyncParams(self.params)
795 89ff748d Thomas Thrainer
    if sync_errors:
796 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
797 89ff748d Thomas Thrainer
                      (self.minor, utils.CommaJoin(sync_errors)))
798 89ff748d Thomas Thrainer
799 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
800 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
801 89ff748d Thomas Thrainer

802 89ff748d Thomas Thrainer
    In case of partially attached (local device matches but no network
803 89ff748d Thomas Thrainer
    setup), we perform the network attach. If successful, we re-test
804 89ff748d Thomas Thrainer
    the attach if can return success.
805 89ff748d Thomas Thrainer

806 89ff748d Thomas Thrainer
    """
807 89ff748d Thomas Thrainer
    # TODO: Rewrite to not use a for loop just because there is 'break'
808 89ff748d Thomas Thrainer
    # pylint: disable=W0631
809 89ff748d Thomas Thrainer
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
810 89ff748d Thomas Thrainer
    for minor in (self._aminor,):
811 27c7d9c3 Thomas Thrainer
      info = self._GetShowInfo(minor)
812 89ff748d Thomas Thrainer
      match_l = self._MatchesLocal(info)
813 89ff748d Thomas Thrainer
      match_r = self._MatchesNet(info)
814 89ff748d Thomas Thrainer
815 89ff748d Thomas Thrainer
      if match_l and match_r:
816 89ff748d Thomas Thrainer
        # everything matches
817 89ff748d Thomas Thrainer
        break
818 89ff748d Thomas Thrainer
819 89ff748d Thomas Thrainer
      if match_l and not match_r and "local_addr" not in info:
820 89ff748d Thomas Thrainer
        # disk matches, but not attached to network, attach and recheck
821 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
822 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
823 27c7d9c3 Thomas Thrainer
        if self._MatchesNet(self._GetShowInfo(minor)):
824 89ff748d Thomas Thrainer
          break
825 89ff748d Thomas Thrainer
        else:
826 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
827 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
828 89ff748d Thomas Thrainer
829 89ff748d Thomas Thrainer
      if match_r and "local_dev" not in info:
830 89ff748d Thomas Thrainer
        # no local disk, but network attached and it matches
831 89ff748d Thomas Thrainer
        self._AssembleLocal(minor, self._children[0].dev_path,
832 89ff748d Thomas Thrainer
                            self._children[1].dev_path, self.size)
833 085b8e24 Thomas Thrainer
        if self._MatchesLocal(self._GetShowInfo(minor)):
834 89ff748d Thomas Thrainer
          break
835 89ff748d Thomas Thrainer
        else:
836 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
837 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
838 89ff748d Thomas Thrainer
839 89ff748d Thomas Thrainer
      # this case must be considered only if we actually have local
840 89ff748d Thomas Thrainer
      # storage, i.e. not in diskless mode, because all diskless
841 89ff748d Thomas Thrainer
      # devices are equal from the point of view of local
842 89ff748d Thomas Thrainer
      # configuration
843 89ff748d Thomas Thrainer
      if (match_l and "local_dev" in info and
844 89ff748d Thomas Thrainer
          not match_r and "local_addr" in info):
845 89ff748d Thomas Thrainer
        # strange case - the device network part points to somewhere
846 89ff748d Thomas Thrainer
        # else, even though its local storage is ours; as we own the
847 89ff748d Thomas Thrainer
        # drbd space, we try to disconnect from the remote peer and
848 89ff748d Thomas Thrainer
        # reconnect to our correct one
849 89ff748d Thomas Thrainer
        try:
850 89ff748d Thomas Thrainer
          self._ShutdownNet(minor)
851 89ff748d Thomas Thrainer
        except errors.BlockDeviceError, err:
852 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: device has correct local storage, wrong"
853 89ff748d Thomas Thrainer
                          " remote peer and is unable to disconnect in order"
854 89ff748d Thomas Thrainer
                          " to attach to the correct peer: %s", minor, str(err))
855 89ff748d Thomas Thrainer
        # note: _AssembleNet also handles the case when we don't want
856 89ff748d Thomas Thrainer
        # local storage (i.e. one or more of the _[lr](host|port) is
857 89ff748d Thomas Thrainer
        # None)
858 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
859 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
860 27c7d9c3 Thomas Thrainer
        if self._MatchesNet(self._GetShowInfo(minor)):
861 89ff748d Thomas Thrainer
          break
862 89ff748d Thomas Thrainer
        else:
863 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
864 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
865 89ff748d Thomas Thrainer
866 89ff748d Thomas Thrainer
    else:
867 89ff748d Thomas Thrainer
      minor = None
868 89ff748d Thomas Thrainer
869 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
870 89ff748d Thomas Thrainer
    if minor is None:
871 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
872 89ff748d Thomas Thrainer
                      self._aminor)
873 89ff748d Thomas Thrainer
874 89ff748d Thomas Thrainer
  def _FastAssemble(self):
875 89ff748d Thomas Thrainer
    """Assemble the drbd device from zero.
876 89ff748d Thomas Thrainer

877 89ff748d Thomas Thrainer
    This is run when in Assemble we detect our minor is unused.
878 89ff748d Thomas Thrainer

879 89ff748d Thomas Thrainer
    """
880 89ff748d Thomas Thrainer
    minor = self._aminor
881 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
882 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
883 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
884 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
885 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
886 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
887 89ff748d Thomas Thrainer
                        constants.DRBD_NET_PROTOCOL,
888 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
889 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
890 89ff748d Thomas Thrainer
891 09a78e1c Thomas Thrainer
  def _ShutdownLocal(self, minor):
892 89ff748d Thomas Thrainer
    """Detach from the local device.
893 89ff748d Thomas Thrainer

894 89ff748d Thomas Thrainer
    I/Os will continue to be served from the remote device. If we
895 89ff748d Thomas Thrainer
    don't have a remote device, this operation will fail.
896 89ff748d Thomas Thrainer

897 47e0abee Thomas Thrainer
    @type minor: int
898 47e0abee Thomas Thrainer
    @param minor: the device to detach from the local device
899 47e0abee Thomas Thrainer

900 89ff748d Thomas Thrainer
    """
901 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenDetachCmd(minor)
902 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
903 89ff748d Thomas Thrainer
    if result.failed:
904 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
905 89ff748d Thomas Thrainer
                      minor, result.output)
906 89ff748d Thomas Thrainer
907 09a78e1c Thomas Thrainer
  def _ShutdownNet(self, minor):
908 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
909 89ff748d Thomas Thrainer

910 89ff748d Thomas Thrainer
    This fails if we don't have a local device.
911 89ff748d Thomas Thrainer

912 47e0abee Thomas Thrainer
    @type minor: boolean
913 47e0abee Thomas Thrainer
    @param minor: the device to disconnect from the remote peer
914 47e0abee Thomas Thrainer

915 89ff748d Thomas Thrainer
    """
916 daec28a7 Thomas Thrainer
    family = self._GetNetFamily(minor, self._lhost, self._rhost)
917 daec28a7 Thomas Thrainer
    cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
918 daec28a7 Thomas Thrainer
                                         self._lhost, self._lport,
919 daec28a7 Thomas Thrainer
                                         self._rhost, self._rport)
920 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
921 89ff748d Thomas Thrainer
    if result.failed:
922 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
923 89ff748d Thomas Thrainer
                      minor, result.output)
924 89ff748d Thomas Thrainer
925 89ff748d Thomas Thrainer
  def Shutdown(self):
926 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
927 89ff748d Thomas Thrainer

928 89ff748d Thomas Thrainer
    """
929 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
930 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
931 89ff748d Thomas Thrainer
      return
932 89ff748d Thomas Thrainer
    minor = self.minor
933 89ff748d Thomas Thrainer
    self.minor = None
934 89ff748d Thomas Thrainer
    self.dev_path = None
935 47e0abee Thomas Thrainer
    DRBD8.ShutdownAll(minor)
936 89ff748d Thomas Thrainer
937 89ff748d Thomas Thrainer
  def Remove(self):
938 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
939 89ff748d Thomas Thrainer

940 89ff748d Thomas Thrainer
    """
941 89ff748d Thomas Thrainer
    self.Shutdown()
942 89ff748d Thomas Thrainer
943 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
944 fd300bc7 Thomas Thrainer
    """Rename a device.
945 fd300bc7 Thomas Thrainer

946 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
947 fd300bc7 Thomas Thrainer

948 fd300bc7 Thomas Thrainer
    """
949 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
950 fd300bc7 Thomas Thrainer
951 47e0abee Thomas Thrainer
  def Grow(self, amount, dryrun, backingstore):
952 47e0abee Thomas Thrainer
    """Resize the DRBD device and its backing storage.
953 47e0abee Thomas Thrainer

954 47e0abee Thomas Thrainer
    See L{BlockDev.Grow} for parameter description.
955 47e0abee Thomas Thrainer

956 47e0abee Thomas Thrainer
    """
957 47e0abee Thomas Thrainer
    if self.minor is None:
958 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
959 47e0abee Thomas Thrainer
    if len(self._children) != 2 or None in self._children:
960 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
961 47e0abee Thomas Thrainer
    self._children[0].Grow(amount, dryrun, backingstore)
962 47e0abee Thomas Thrainer
    if dryrun or backingstore:
963 47e0abee Thomas Thrainer
      # DRBD does not support dry-run mode and is not backing storage,
964 47e0abee Thomas Thrainer
      # so we'll return here
965 47e0abee Thomas Thrainer
      return
966 47e0abee Thomas Thrainer
    cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
967 47e0abee Thomas Thrainer
    result = utils.RunCmd(cmd)
968 47e0abee Thomas Thrainer
    if result.failed:
969 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
970 47e0abee Thomas Thrainer
971 47e0abee Thomas Thrainer
  @classmethod
972 47e0abee Thomas Thrainer
  def _InitMeta(cls, minor, dev_path):
973 47e0abee Thomas Thrainer
    """Initialize a meta device.
974 47e0abee Thomas Thrainer

975 47e0abee Thomas Thrainer
    This will not work if the given minor is in use.
976 47e0abee Thomas Thrainer

977 47e0abee Thomas Thrainer
    @type minor: int
978 47e0abee Thomas Thrainer
    @param minor: the DRBD minor whose (future) meta device should be
979 47e0abee Thomas Thrainer
      initialized
980 47e0abee Thomas Thrainer
    @type dev_path: string
981 47e0abee Thomas Thrainer
    @param dev_path: path to the meta device to initialize
982 47e0abee Thomas Thrainer

983 47e0abee Thomas Thrainer
    """
984 47e0abee Thomas Thrainer
    # Zero the metadata first, in order to make sure drbdmeta doesn't
985 47e0abee Thomas Thrainer
    # try to auto-detect existing filesystems or similar (see
986 47e0abee Thomas Thrainer
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
987 47e0abee Thomas Thrainer
    # care about the first 128MB of data in the device, even though it
988 47e0abee Thomas Thrainer
    # can be bigger
989 47e0abee Thomas Thrainer
    result = utils.RunCmd([constants.DD_CMD,
990 47e0abee Thomas Thrainer
                           "if=/dev/zero", "of=%s" % dev_path,
991 47e0abee Thomas Thrainer
                           "bs=1048576", "count=128", "oflag=direct"])
992 47e0abee Thomas Thrainer
    if result.failed:
993 47e0abee Thomas Thrainer
      base.ThrowError("Can't wipe the meta device: %s", result.output)
994 47e0abee Thomas Thrainer
995 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
996 47e0abee Thomas Thrainer
    cmd_gen = DRBD8.GetCmdGenerator(info)
997 47e0abee Thomas Thrainer
    cmd = cmd_gen.GenInitMetaCmd(minor, dev_path)
998 47e0abee Thomas Thrainer
999 47e0abee Thomas Thrainer
    result = utils.RunCmd(cmd)
1000 47e0abee Thomas Thrainer
    if result.failed:
1001 47e0abee Thomas Thrainer
      base.ThrowError("Can't initialize meta device: %s", result.output)
1002 47e0abee Thomas Thrainer
1003 89ff748d Thomas Thrainer
  @classmethod
1004 24c06acb Bernardo Dal Seno
  def Create(cls, unique_id, children, size, spindles, params, excl_stor):
1005 89ff748d Thomas Thrainer
    """Create a new DRBD8 device.
1006 89ff748d Thomas Thrainer

1007 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
1008 89ff748d Thomas Thrainer
    function only initializes the metadata.
1009 89ff748d Thomas Thrainer

1010 89ff748d Thomas Thrainer
    """
1011 89ff748d Thomas Thrainer
    if len(children) != 2:
1012 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1013 89ff748d Thomas Thrainer
    if excl_stor:
1014 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("DRBD device requested with"
1015 89ff748d Thomas Thrainer
                                   " exclusive_storage")
1016 89ff748d Thomas Thrainer
    # check that the minor is unused
1017 89ff748d Thomas Thrainer
    aminor = unique_id[4]
1018 2fe690f1 Thomas Thrainer
1019 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
1020 47e0abee Thomas Thrainer
    if info.HasMinorStatus(aminor):
1021 47e0abee Thomas Thrainer
      status = info.GetMinorStatus(aminor)
1022 89ff748d Thomas Thrainer
      in_use = status.is_in_use
1023 89ff748d Thomas Thrainer
    else:
1024 89ff748d Thomas Thrainer
      in_use = False
1025 89ff748d Thomas Thrainer
    if in_use:
1026 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: minor is already in use at Create() time",
1027 89ff748d Thomas Thrainer
                      aminor)
1028 89ff748d Thomas Thrainer
    meta = children[1]
1029 89ff748d Thomas Thrainer
    meta.Assemble()
1030 89ff748d Thomas Thrainer
    if not meta.Attach():
1031 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to meta device '%s'",
1032 89ff748d Thomas Thrainer
                      aminor, meta)
1033 89ff748d Thomas Thrainer
    cls._CheckMetaSize(meta.dev_path)
1034 89ff748d Thomas Thrainer
    cls._InitMeta(aminor, meta.dev_path)
1035 89ff748d Thomas Thrainer
    return cls(unique_id, children, size, params)
1036 89ff748d Thomas Thrainer
1037 89ff748d Thomas Thrainer
1038 89ff748d Thomas Thrainer
def _CanReadDevice(path):
1039 89ff748d Thomas Thrainer
  """Check if we can read from the given device.
1040 89ff748d Thomas Thrainer

1041 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
1042 89ff748d Thomas Thrainer

1043 47e0abee Thomas Thrainer
  @type path: string
1044 47e0abee Thomas Thrainer

1045 89ff748d Thomas Thrainer
  """
1046 89ff748d Thomas Thrainer
  try:
1047 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1048 89ff748d Thomas Thrainer
    return True
1049 89ff748d Thomas Thrainer
  except EnvironmentError:
1050 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
1051 89ff748d Thomas Thrainer
    return False