Statistics
| Branch: | Tag: | Revision:

root / lib / storage / drbd.py @ 653bc0f1

History | View | Annotate | Download (35.8 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 0c3d9c7c Thomas Thrainer
  The unique_id for the drbd device is a (pnode_uuid, snode_uuid,
167 0c3d9c7c Thomas Thrainer
  port, pnode_minor, lnode_minor, secret) tuple, and it must have
168 0c3d9c7c Thomas Thrainer
  two children: the data device and the meta_device. The meta
169 0c3d9c7c Thomas Thrainer
  device 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 c032b2ce Klaus Aehlig
  def __init__(self, unique_id, children, size, params, dyn_params, *args):
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 0c3d9c7c Thomas Thrainer
    if constants.DDP_LOCAL_IP not in dyn_params or \
185 0c3d9c7c Thomas Thrainer
       constants.DDP_REMOTE_IP not in dyn_params or \
186 0c3d9c7c Thomas Thrainer
       constants.DDP_LOCAL_MINOR not in dyn_params or \
187 0c3d9c7c Thomas Thrainer
       constants.DDP_REMOTE_MINOR not in dyn_params:
188 0c3d9c7c Thomas Thrainer
      raise ValueError("Invalid dynamic parameters %s" % str(dyn_params))
189 0c3d9c7c Thomas Thrainer
190 0c3d9c7c Thomas Thrainer
    self._lhost = dyn_params[constants.DDP_LOCAL_IP]
191 0c3d9c7c Thomas Thrainer
    self._lport = unique_id[2]
192 0c3d9c7c Thomas Thrainer
    self._rhost = dyn_params[constants.DDP_REMOTE_IP]
193 0c3d9c7c Thomas Thrainer
    self._rport = unique_id[2]
194 0c3d9c7c Thomas Thrainer
    self._aminor = dyn_params[constants.DDP_LOCAL_MINOR]
195 0c3d9c7c Thomas Thrainer
    self._secret = unique_id[5]
196 0c3d9c7c Thomas Thrainer
197 fd300bc7 Thomas Thrainer
    if children:
198 fd300bc7 Thomas Thrainer
      if not _CanReadDevice(children[1].dev_path):
199 fd300bc7 Thomas Thrainer
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
200 fd300bc7 Thomas Thrainer
        children = []
201 0c3d9c7c Thomas Thrainer
    super(DRBD8Dev, self).__init__(unique_id, children, size, params,
202 c032b2ce Klaus Aehlig
                                   dyn_params, *args)
203 fd300bc7 Thomas Thrainer
    self.major = self._DRBD_MAJOR
204 2fe690f1 Thomas Thrainer
205 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
206 47e0abee Thomas Thrainer
    version = info.GetVersion()
207 fd300bc7 Thomas Thrainer
    if version["k_major"] != 8:
208 fd300bc7 Thomas Thrainer
      base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
209 fd300bc7 Thomas Thrainer
                      " usage: kernel is %s.%s, ganeti wants 8.x",
210 fd300bc7 Thomas Thrainer
                      version["k_major"], version["k_minor"])
211 fd300bc7 Thomas Thrainer
212 27c7d9c3 Thomas Thrainer
    if version["k_minor"] <= 3:
213 47e0abee Thomas Thrainer
      self._show_info_cls = drbd_info.DRBD83ShowInfo
214 27c7d9c3 Thomas Thrainer
    else:
215 47e0abee Thomas Thrainer
      self._show_info_cls = drbd_info.DRBD84ShowInfo
216 daec28a7 Thomas Thrainer
217 47e0abee Thomas Thrainer
    self._cmd_gen = DRBD8.GetCmdGenerator(info)
218 27c7d9c3 Thomas Thrainer
219 fd300bc7 Thomas Thrainer
    if (self._lhost is not None and self._lhost == self._rhost and
220 fd300bc7 Thomas Thrainer
            self._lport == self._rport):
221 0c3d9c7c Thomas Thrainer
      raise ValueError("Invalid configuration data, same local/remote %s, %s" %
222 0c3d9c7c Thomas Thrainer
                       (unique_id, dyn_params))
223 fd300bc7 Thomas Thrainer
    self.Attach()
224 fd300bc7 Thomas Thrainer
225 89ff748d Thomas Thrainer
  @staticmethod
226 89ff748d Thomas Thrainer
  def _DevPath(minor):
227 89ff748d Thomas Thrainer
    """Return the path to a drbd device for a given minor.
228 89ff748d Thomas Thrainer

229 47e0abee Thomas Thrainer
    @type minor: int
230 47e0abee Thomas Thrainer
    @rtype: string
231 89ff748d Thomas Thrainer

232 89ff748d Thomas Thrainer
    """
233 47e0abee Thomas Thrainer
    return "/dev/drbd%d" % minor
234 89ff748d Thomas Thrainer
235 89ff748d Thomas Thrainer
  def _SetFromMinor(self, minor):
236 89ff748d Thomas Thrainer
    """Set our parameters based on the given minor.
237 89ff748d Thomas Thrainer

238 89ff748d Thomas Thrainer
    This sets our minor variable and our dev_path.
239 89ff748d Thomas Thrainer

240 47e0abee Thomas Thrainer
    @type minor: int
241 47e0abee Thomas Thrainer

242 89ff748d Thomas Thrainer
    """
243 89ff748d Thomas Thrainer
    if minor is None:
244 89ff748d Thomas Thrainer
      self.minor = self.dev_path = None
245 89ff748d Thomas Thrainer
      self.attached = False
246 89ff748d Thomas Thrainer
    else:
247 89ff748d Thomas Thrainer
      self.minor = minor
248 89ff748d Thomas Thrainer
      self.dev_path = self._DevPath(minor)
249 89ff748d Thomas Thrainer
      self.attached = True
250 89ff748d Thomas Thrainer
251 89ff748d Thomas Thrainer
  @staticmethod
252 89ff748d Thomas Thrainer
  def _CheckMetaSize(meta_device):
253 89ff748d Thomas Thrainer
    """Check if the given meta device looks like a valid one.
254 89ff748d Thomas Thrainer

255 89ff748d Thomas Thrainer
    This currently only checks the size, which must be around
256 89ff748d Thomas Thrainer
    128MiB.
257 89ff748d Thomas Thrainer

258 47e0abee Thomas Thrainer
    @type meta_device: string
259 47e0abee Thomas Thrainer
    @param meta_device: the path to the device to check
260 47e0abee Thomas Thrainer

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

287 47e0abee Thomas Thrainer
    @type minor: int
288 47e0abee Thomas Thrainer
    @param minor: the minor to collect show output for
289 47e0abee Thomas Thrainer
    @rtype: string
290 89ff748d Thomas Thrainer

291 89ff748d Thomas Thrainer
    """
292 09a78e1c Thomas Thrainer
    result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
293 89ff748d Thomas Thrainer
    if result.failed:
294 89ff748d Thomas Thrainer
      logging.error("Can't display the drbd config: %s - %s",
295 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
296 89ff748d Thomas Thrainer
      return None
297 89ff748d Thomas Thrainer
    return result.stdout
298 89ff748d Thomas Thrainer
299 27c7d9c3 Thomas Thrainer
  def _GetShowInfo(self, minor):
300 47e0abee Thomas Thrainer
    """Return parsed information from `drbdsetup show`.
301 47e0abee Thomas Thrainer

302 47e0abee Thomas Thrainer
    @type minor: int
303 47e0abee Thomas Thrainer
    @param minor: the minor to return information for
304 47e0abee Thomas Thrainer
    @rtype: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
305 47e0abee Thomas Thrainer

306 47e0abee Thomas Thrainer
    """
307 27c7d9c3 Thomas Thrainer
    return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
308 27c7d9c3 Thomas Thrainer
309 89ff748d Thomas Thrainer
  def _MatchesLocal(self, info):
310 89ff748d Thomas Thrainer
    """Test if our local config matches with an existing device.
311 89ff748d Thomas Thrainer

312 47e0abee Thomas Thrainer
    The parameter should be as returned from `_GetShowInfo()`. This
313 89ff748d Thomas Thrainer
    method tests if our local backing device is the same as the one in
314 89ff748d Thomas Thrainer
    the info parameter, in effect testing if we look like the given
315 89ff748d Thomas Thrainer
    device.
316 89ff748d Thomas Thrainer

317 47e0abee Thomas Thrainer
    @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
318 47e0abee Thomas Thrainer
    @rtype: boolean
319 47e0abee Thomas Thrainer

320 89ff748d Thomas Thrainer
    """
321 89ff748d Thomas Thrainer
    if self._children:
322 89ff748d Thomas Thrainer
      backend, meta = self._children
323 89ff748d Thomas Thrainer
    else:
324 89ff748d Thomas Thrainer
      backend = meta = None
325 89ff748d Thomas Thrainer
326 89ff748d Thomas Thrainer
    if backend is not None:
327 89ff748d Thomas Thrainer
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
328 89ff748d Thomas Thrainer
    else:
329 89ff748d Thomas Thrainer
      retval = ("local_dev" not in info)
330 89ff748d Thomas Thrainer
331 89ff748d Thomas Thrainer
    if meta is not None:
332 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" in info and
333 89ff748d Thomas Thrainer
                           info["meta_dev"] == meta.dev_path)
334 085b8e24 Thomas Thrainer
      if "meta_index" in info:
335 085b8e24 Thomas Thrainer
        retval = retval and info["meta_index"] == 0
336 89ff748d Thomas Thrainer
    else:
337 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" not in info and
338 89ff748d Thomas Thrainer
                           "meta_index" not in info)
339 89ff748d Thomas Thrainer
    return retval
340 89ff748d Thomas Thrainer
341 89ff748d Thomas Thrainer
  def _MatchesNet(self, info):
342 89ff748d Thomas Thrainer
    """Test if our network config matches with an existing device.
343 89ff748d Thomas Thrainer

344 47e0abee Thomas Thrainer
    The parameter should be as returned from `_GetShowInfo()`. This
345 89ff748d Thomas Thrainer
    method tests if our network configuration is the same as the one
346 89ff748d Thomas Thrainer
    in the info parameter, in effect testing if we look like the given
347 89ff748d Thomas Thrainer
    device.
348 89ff748d Thomas Thrainer

349 47e0abee Thomas Thrainer
    @type info: dict as described in L{drbd_info.BaseShowInfo.GetDevInfo}
350 47e0abee Thomas Thrainer
    @rtype: boolean
351 47e0abee Thomas Thrainer

352 89ff748d Thomas Thrainer
    """
353 89ff748d Thomas Thrainer
    if (((self._lhost is None and not ("local_addr" in info)) and
354 89ff748d Thomas Thrainer
         (self._rhost is None and not ("remote_addr" in info)))):
355 89ff748d Thomas Thrainer
      return True
356 89ff748d Thomas Thrainer
357 89ff748d Thomas Thrainer
    if self._lhost is None:
358 89ff748d Thomas Thrainer
      return False
359 89ff748d Thomas Thrainer
360 89ff748d Thomas Thrainer
    if not ("local_addr" in info and
361 89ff748d Thomas Thrainer
            "remote_addr" in info):
362 89ff748d Thomas Thrainer
      return False
363 89ff748d Thomas Thrainer
364 89ff748d Thomas Thrainer
    retval = (info["local_addr"] == (self._lhost, self._lport))
365 89ff748d Thomas Thrainer
    retval = (retval and
366 89ff748d Thomas Thrainer
              info["remote_addr"] == (self._rhost, self._rport))
367 89ff748d Thomas Thrainer
    return retval
368 89ff748d Thomas Thrainer
369 89ff748d Thomas Thrainer
  def _AssembleLocal(self, minor, backend, meta, size):
370 89ff748d Thomas Thrainer
    """Configure the local part of a DRBD device.
371 89ff748d Thomas Thrainer

372 47e0abee Thomas Thrainer
    @type minor: int
373 47e0abee Thomas Thrainer
    @param minor: the minor to assemble locally
374 47e0abee Thomas Thrainer
    @type backend: string
375 47e0abee Thomas Thrainer
    @param backend: path to the data device to use
376 47e0abee Thomas Thrainer
    @type meta: string
377 47e0abee Thomas Thrainer
    @param meta: path to the meta device to use
378 47e0abee Thomas Thrainer
    @type size: int
379 47e0abee Thomas Thrainer
    @param size: size in MiB
380 47e0abee Thomas Thrainer

381 89ff748d Thomas Thrainer
    """
382 09a78e1c Thomas Thrainer
    cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta,
383 09a78e1c Thomas Thrainer
                                          size, self.params)
384 89ff748d Thomas Thrainer
385 09a78e1c Thomas Thrainer
    for cmd in cmds:
386 09a78e1c Thomas Thrainer
      result = utils.RunCmd(cmd)
387 09a78e1c Thomas Thrainer
      if result.failed:
388 09a78e1c Thomas Thrainer
        base.ThrowError("drbd%d: can't attach local disk: %s",
389 09a78e1c Thomas Thrainer
                        minor, result.output)
390 89ff748d Thomas Thrainer
391 65fc2388 Thomas Thrainer
  def _AssembleNet(self, minor, net_info, dual_pri=False, hmac=None,
392 65fc2388 Thomas Thrainer
                   secret=None):
393 89ff748d Thomas Thrainer
    """Configure the network part of the device.
394 89ff748d Thomas Thrainer

395 47e0abee Thomas Thrainer
    @type minor: int
396 47e0abee Thomas Thrainer
    @param minor: the minor to assemble the network for
397 47e0abee Thomas Thrainer
    @type net_info: (string, int, string, int)
398 47e0abee Thomas Thrainer
    @param net_info: tuple containing the local address, local port, remote
399 47e0abee Thomas Thrainer
      address and remote port
400 47e0abee Thomas Thrainer
    @type dual_pri: boolean
401 47e0abee Thomas Thrainer
    @param dual_pri: whether two primaries should be allowed or not
402 47e0abee Thomas Thrainer
    @type hmac: string
403 47e0abee Thomas Thrainer
    @param hmac: the HMAC algorithm to use
404 47e0abee Thomas Thrainer
    @type secret: string
405 47e0abee Thomas Thrainer
    @param secret: the shared secret to use
406 47e0abee Thomas Thrainer

407 89ff748d Thomas Thrainer
    """
408 89ff748d Thomas Thrainer
    lhost, lport, rhost, rport = net_info
409 89ff748d Thomas Thrainer
    if None in net_info:
410 89ff748d Thomas Thrainer
      # we don't want network connection and actually want to make
411 89ff748d Thomas Thrainer
      # sure its shutdown
412 89ff748d Thomas Thrainer
      self._ShutdownNet(minor)
413 89ff748d Thomas Thrainer
      return
414 89ff748d Thomas Thrainer
415 65fc2388 Thomas Thrainer
    if dual_pri:
416 65fc2388 Thomas Thrainer
      protocol = constants.DRBD_MIGRATION_NET_PROTOCOL
417 65fc2388 Thomas Thrainer
    else:
418 65fc2388 Thomas Thrainer
      protocol = self.params[constants.LDP_PROTOCOL]
419 65fc2388 Thomas Thrainer
420 89ff748d Thomas Thrainer
    # Workaround for a race condition. When DRBD is doing its dance to
421 89ff748d Thomas Thrainer
    # establish a connection with its peer, it also sends the
422 89ff748d Thomas Thrainer
    # synchronization speed over the wire. In some cases setting the
423 89ff748d Thomas Thrainer
    # sync speed only after setting up both sides can race with DRBD
424 89ff748d Thomas Thrainer
    # connecting, hence we set it here before telling DRBD anything
425 89ff748d Thomas Thrainer
    # about its peer.
426 89ff748d Thomas Thrainer
    sync_errors = self._SetMinorSyncParams(minor, self.params)
427 89ff748d Thomas Thrainer
    if sync_errors:
428 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
429 89ff748d Thomas Thrainer
                      (minor, utils.CommaJoin(sync_errors)))
430 89ff748d Thomas Thrainer
431 daec28a7 Thomas Thrainer
    family = self._GetNetFamily(minor, lhost, rhost)
432 89ff748d Thomas Thrainer
433 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
434 09a78e1c Thomas Thrainer
                                      rhost, rport, protocol,
435 09a78e1c Thomas Thrainer
                                      dual_pri, hmac, secret, self.params)
436 09a78e1c Thomas Thrainer
437 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
438 89ff748d Thomas Thrainer
    if result.failed:
439 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't setup network: %s - %s",
440 89ff748d Thomas Thrainer
                      minor, result.fail_reason, result.output)
441 89ff748d Thomas Thrainer
442 89ff748d Thomas Thrainer
    def _CheckNetworkConfig():
443 27c7d9c3 Thomas Thrainer
      info = self._GetShowInfo(minor)
444 89ff748d Thomas Thrainer
      if not "local_addr" in info or not "remote_addr" in info:
445 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
446 89ff748d Thomas Thrainer
447 89ff748d Thomas Thrainer
      if (info["local_addr"] != (lhost, lport) or
448 89ff748d Thomas Thrainer
          info["remote_addr"] != (rhost, rport)):
449 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
450 89ff748d Thomas Thrainer
451 89ff748d Thomas Thrainer
    try:
452 89ff748d Thomas Thrainer
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
453 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
454 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: timeout while configuring network", minor)
455 89ff748d Thomas Thrainer
456 1a705513 Hrvoje Ribicic
    # Once the assembly is over, try to set the synchronization parameters
457 1a705513 Hrvoje Ribicic
    try:
458 1a705513 Hrvoje Ribicic
      # The minor may not have been set yet, requiring us to set it at least
459 1a705513 Hrvoje Ribicic
      # temporarily
460 1a705513 Hrvoje Ribicic
      old_minor = self.minor
461 1a705513 Hrvoje Ribicic
      self._SetFromMinor(minor)
462 1a705513 Hrvoje Ribicic
      sync_errors = self.SetSyncParams(self.params)
463 1a705513 Hrvoje Ribicic
      if sync_errors:
464 1a705513 Hrvoje Ribicic
        base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
465 1a705513 Hrvoje Ribicic
                        (self.minor, utils.CommaJoin(sync_errors)))
466 1a705513 Hrvoje Ribicic
    finally:
467 1a705513 Hrvoje Ribicic
      # Undo the change, regardless of whether it will have to be done again
468 1a705513 Hrvoje Ribicic
      # soon
469 1a705513 Hrvoje Ribicic
      self._SetFromMinor(old_minor)
470 1a705513 Hrvoje Ribicic
471 daec28a7 Thomas Thrainer
  @staticmethod
472 daec28a7 Thomas Thrainer
  def _GetNetFamily(minor, lhost, rhost):
473 daec28a7 Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
474 daec28a7 Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
475 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
476 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
477 daec28a7 Thomas Thrainer
      return "ipv6"
478 daec28a7 Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
479 daec28a7 Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
480 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
481 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
482 daec28a7 Thomas Thrainer
      return "ipv4"
483 daec28a7 Thomas Thrainer
    else:
484 daec28a7 Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
485 daec28a7 Thomas Thrainer
486 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
487 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
488 89ff748d Thomas Thrainer

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

493 89ff748d Thomas Thrainer
    """
494 89ff748d Thomas Thrainer
    if self.minor is None:
495 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
496 89ff748d Thomas Thrainer
                      self._aminor)
497 89ff748d Thomas Thrainer
    if len(devices) != 2:
498 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
499 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
500 89ff748d Thomas Thrainer
    if "local_dev" in info:
501 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
502 89ff748d Thomas Thrainer
    backend, meta = devices
503 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
504 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
505 89ff748d Thomas Thrainer
                      self.minor)
506 89ff748d Thomas Thrainer
    backend.Open()
507 89ff748d Thomas Thrainer
    meta.Open()
508 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
509 47e0abee Thomas Thrainer
    self._InitMeta(DRBD8.FindUnusedMinor(), meta.dev_path)
510 89ff748d Thomas Thrainer
511 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
512 89ff748d Thomas Thrainer
    self._children = devices
513 89ff748d Thomas Thrainer
514 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
515 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
516 89ff748d Thomas Thrainer

517 47e0abee Thomas Thrainer
    @type devices: list of L{BlockDev}
518 47e0abee Thomas Thrainer
    @param devices: a list of exactly two L{BlockDev} objects; the first
519 47e0abee Thomas Thrainer
      denotes the data device, the second the meta device for this DRBD device
520 47e0abee Thomas Thrainer

521 89ff748d Thomas Thrainer
    """
522 89ff748d Thomas Thrainer
    if self.minor is None:
523 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
524 89ff748d Thomas Thrainer
                      self._aminor)
525 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
526 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
527 89ff748d Thomas Thrainer
    if "local_dev" not in info:
528 89ff748d Thomas Thrainer
      return
529 89ff748d Thomas Thrainer
    if len(self._children) != 2:
530 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
531 89ff748d Thomas Thrainer
                      self._children)
532 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
533 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
534 89ff748d Thomas Thrainer
      return
535 89ff748d Thomas Thrainer
    if len(devices) != 2:
536 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
537 89ff748d Thomas Thrainer
                      self.minor)
538 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
539 89ff748d Thomas Thrainer
      if dev != child.dev_path:
540 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
541 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
542 89ff748d Thomas Thrainer
543 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
544 89ff748d Thomas Thrainer
    self._children = []
545 89ff748d Thomas Thrainer
546 2fe690f1 Thomas Thrainer
  def _SetMinorSyncParams(self, minor, params):
547 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
548 89ff748d Thomas Thrainer

549 89ff748d Thomas Thrainer
    This is the low-level implementation.
550 89ff748d Thomas Thrainer

551 89ff748d Thomas Thrainer
    @type minor: int
552 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
553 89ff748d Thomas Thrainer
    @type params: dict
554 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
555 89ff748d Thomas Thrainer
    @rtype: list
556 89ff748d Thomas Thrainer
    @return: a list of error messages
557 89ff748d Thomas Thrainer

558 89ff748d Thomas Thrainer
    """
559 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
560 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
561 89ff748d Thomas Thrainer
    if result.failed:
562 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
563 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
564 89ff748d Thomas Thrainer
      logging.error(msg)
565 89ff748d Thomas Thrainer
      return [msg]
566 89ff748d Thomas Thrainer
567 89ff748d Thomas Thrainer
    return []
568 89ff748d Thomas Thrainer
569 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
570 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
571 89ff748d Thomas Thrainer

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

574 89ff748d Thomas Thrainer
    """
575 89ff748d Thomas Thrainer
    if self.minor is None:
576 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
577 89ff748d Thomas Thrainer
      logging.info(err)
578 89ff748d Thomas Thrainer
      return [err]
579 89ff748d Thomas Thrainer
580 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).SetSyncParams(params)
581 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
582 89ff748d Thomas Thrainer
    return children_result
583 89ff748d Thomas Thrainer
584 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
585 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
586 89ff748d Thomas Thrainer

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

589 89ff748d Thomas Thrainer
    """
590 89ff748d Thomas Thrainer
    if self.minor is None:
591 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
592 89ff748d Thomas Thrainer
      return False
593 89ff748d Thomas Thrainer
594 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).PauseResumeSync(pause)
595 89ff748d Thomas Thrainer
596 89ff748d Thomas Thrainer
    if pause:
597 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
598 89ff748d Thomas Thrainer
    else:
599 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
600 89ff748d Thomas Thrainer
601 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
602 89ff748d Thomas Thrainer
    if result.failed:
603 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
604 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
605 89ff748d Thomas Thrainer
    return not result.failed and children_result
606 89ff748d Thomas Thrainer
607 89ff748d Thomas Thrainer
  def GetProcStatus(self):
608 47e0abee Thomas Thrainer
    """Return the current status data from /proc/drbd for this device.
609 47e0abee Thomas Thrainer

610 47e0abee Thomas Thrainer
    @rtype: DRBD8Status
611 89ff748d Thomas Thrainer

612 89ff748d Thomas Thrainer
    """
613 89ff748d Thomas Thrainer
    if self.minor is None:
614 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
615 89ff748d Thomas Thrainer
                      self._aminor)
616 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
617 47e0abee Thomas Thrainer
    if not info.HasMinorStatus(self.minor):
618 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
619 47e0abee Thomas Thrainer
    return info.GetMinorStatus(self.minor)
620 89ff748d Thomas Thrainer
621 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
622 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
623 89ff748d Thomas Thrainer

624 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
625 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
626 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
627 89ff748d Thomas Thrainer

628 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
629 89ff748d Thomas Thrainer
    network not connected or local disk missing.
630 89ff748d Thomas Thrainer

631 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
632 89ff748d Thomas Thrainer
    disk or not.
633 89ff748d Thomas Thrainer

634 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
635 89ff748d Thomas Thrainer

636 89ff748d Thomas Thrainer
    """
637 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
638 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
639 89ff748d Thomas Thrainer
640 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
641 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
642 89ff748d Thomas Thrainer
643 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
644 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
645 89ff748d Thomas Thrainer
    elif stats.is_diskless:
646 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
647 89ff748d Thomas Thrainer
    else:
648 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
649 89ff748d Thomas Thrainer
650 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
651 89ff748d Thomas Thrainer
                                  major=self.major,
652 89ff748d Thomas Thrainer
                                  minor=self.minor,
653 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
654 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
655 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
656 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
657 89ff748d Thomas Thrainer
658 89ff748d Thomas Thrainer
  def Open(self, force=False):
659 89ff748d Thomas Thrainer
    """Make the local state primary.
660 89ff748d Thomas Thrainer

661 47e0abee Thomas Thrainer
    If the 'force' parameter is given, DRBD is instructed to switch the device
662 47e0abee Thomas Thrainer
    into primary mode. Since this is a potentially dangerous operation, the
663 47e0abee Thomas Thrainer
    force flag should be only given after creation, when it actually is
664 47e0abee Thomas Thrainer
    mandatory.
665 89ff748d Thomas Thrainer

666 89ff748d Thomas Thrainer
    """
667 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
668 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
669 89ff748d Thomas Thrainer
      return False
670 09a78e1c Thomas Thrainer
671 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
672 09a78e1c Thomas Thrainer
673 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
674 89ff748d Thomas Thrainer
    if result.failed:
675 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
676 89ff748d Thomas Thrainer
                      result.output)
677 89ff748d Thomas Thrainer
678 89ff748d Thomas Thrainer
  def Close(self):
679 89ff748d Thomas Thrainer
    """Make the local state secondary.
680 89ff748d Thomas Thrainer

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

683 89ff748d Thomas Thrainer
    """
684 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
685 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
686 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
687 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
688 89ff748d Thomas Thrainer
    if result.failed:
689 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
690 89ff748d Thomas Thrainer
                      self.minor, result.output)
691 89ff748d Thomas Thrainer
692 89ff748d Thomas Thrainer
  def DisconnectNet(self):
693 89ff748d Thomas Thrainer
    """Removes network configuration.
694 89ff748d Thomas Thrainer

695 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
696 89ff748d Thomas Thrainer

697 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
698 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
699 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
700 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
701 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
702 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
703 89ff748d Thomas Thrainer
    exception.
704 89ff748d Thomas Thrainer

705 89ff748d Thomas Thrainer
    """
706 89ff748d Thomas Thrainer
    if self.minor is None:
707 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: disk not attached in re-attach net",
708 89ff748d Thomas Thrainer
                      self._aminor)
709 89ff748d Thomas Thrainer
710 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
711 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: DRBD disk missing network info in"
712 89ff748d Thomas Thrainer
                      " DisconnectNet()", self.minor)
713 89ff748d Thomas Thrainer
714 89ff748d Thomas Thrainer
    class _DisconnectStatus:
715 89ff748d Thomas Thrainer
      def __init__(self, ever_disconnected):
716 89ff748d Thomas Thrainer
        self.ever_disconnected = ever_disconnected
717 89ff748d Thomas Thrainer
718 89ff748d Thomas Thrainer
    dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
719 89ff748d Thomas Thrainer
720 89ff748d Thomas Thrainer
    def _WaitForDisconnect():
721 89ff748d Thomas Thrainer
      if self.GetProcStatus().is_standalone:
722 89ff748d Thomas Thrainer
        return
723 89ff748d Thomas Thrainer
724 89ff748d Thomas Thrainer
      # retry the disconnect, it seems possible that due to a well-time
725 89ff748d Thomas Thrainer
      # disconnect on the peer, my disconnect command might be ignored and
726 89ff748d Thomas Thrainer
      # forgotten
727 89ff748d Thomas Thrainer
      dstatus.ever_disconnected = \
728 89ff748d Thomas Thrainer
        base.IgnoreError(self._ShutdownNet, self.minor) or \
729 89ff748d Thomas Thrainer
        dstatus.ever_disconnected
730 89ff748d Thomas Thrainer
731 89ff748d Thomas Thrainer
      raise utils.RetryAgain()
732 89ff748d Thomas Thrainer
733 89ff748d Thomas Thrainer
    # Keep start time
734 89ff748d Thomas Thrainer
    start_time = time.time()
735 89ff748d Thomas Thrainer
736 89ff748d Thomas Thrainer
    try:
737 89ff748d Thomas Thrainer
      # Start delay at 100 milliseconds and grow up to 2 seconds
738 89ff748d Thomas Thrainer
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
739 89ff748d Thomas Thrainer
                  self._NET_RECONFIG_TIMEOUT)
740 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
741 89ff748d Thomas Thrainer
      if dstatus.ever_disconnected:
742 89ff748d Thomas Thrainer
        msg = ("drbd%d: device did not react to the"
743 89ff748d Thomas Thrainer
               " 'disconnect' command in a timely manner")
744 89ff748d Thomas Thrainer
      else:
745 89ff748d Thomas Thrainer
        msg = "drbd%d: can't shutdown network, even after multiple retries"
746 89ff748d Thomas Thrainer
747 89ff748d Thomas Thrainer
      base.ThrowError(msg, self.minor)
748 89ff748d Thomas Thrainer
749 89ff748d Thomas Thrainer
    reconfig_time = time.time() - start_time
750 89ff748d Thomas Thrainer
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
751 89ff748d Thomas Thrainer
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
752 89ff748d Thomas Thrainer
                   self.minor, reconfig_time)
753 89ff748d Thomas Thrainer
754 89ff748d Thomas Thrainer
  def AttachNet(self, multimaster):
755 89ff748d Thomas Thrainer
    """Reconnects the network.
756 89ff748d Thomas Thrainer

757 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
758 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
759 89ff748d Thomas Thrainer
    but have valid network configuration data.
760 89ff748d Thomas Thrainer

761 47e0abee Thomas Thrainer
    @type multimaster: boolean
762 47e0abee Thomas Thrainer
    @param multimaster: init the network in dual-primary mode
763 89ff748d Thomas Thrainer

764 89ff748d Thomas Thrainer
    """
765 89ff748d Thomas Thrainer
    if self.minor is None:
766 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
767 89ff748d Thomas Thrainer
768 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
769 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
770 89ff748d Thomas Thrainer
771 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
772 89ff748d Thomas Thrainer
773 89ff748d Thomas Thrainer
    if not status.is_standalone:
774 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
775 89ff748d Thomas Thrainer
                      self.minor)
776 89ff748d Thomas Thrainer
777 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
778 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
779 65fc2388 Thomas Thrainer
                      dual_pri=multimaster, hmac=constants.DRBD_HMAC_ALG,
780 65fc2388 Thomas Thrainer
                      secret=self._secret)
781 89ff748d Thomas Thrainer
782 89ff748d Thomas Thrainer
  def Attach(self):
783 89ff748d Thomas Thrainer
    """Check if our minor is configured.
784 89ff748d Thomas Thrainer

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

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

792 89ff748d Thomas Thrainer
    """
793 47e0abee Thomas Thrainer
    used_devs = DRBD8.GetUsedDevs()
794 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
795 89ff748d Thomas Thrainer
      minor = self._aminor
796 89ff748d Thomas Thrainer
    else:
797 89ff748d Thomas Thrainer
      minor = None
798 89ff748d Thomas Thrainer
799 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
800 89ff748d Thomas Thrainer
    return minor is not None
801 89ff748d Thomas Thrainer
802 89ff748d Thomas Thrainer
  def Assemble(self):
803 89ff748d Thomas Thrainer
    """Assemble the drbd.
804 89ff748d Thomas Thrainer

805 89ff748d Thomas Thrainer
    Method:
806 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
807 89ff748d Thomas Thrainer
        our config
808 89ff748d Thomas Thrainer
      - if not, we create it from zero
809 89ff748d Thomas Thrainer
      - anyway, set the device parameters
810 89ff748d Thomas Thrainer

811 89ff748d Thomas Thrainer
    """
812 239364d0 Thomas Thrainer
    super(DRBD8Dev, self).Assemble()
813 89ff748d Thomas Thrainer
814 89ff748d Thomas Thrainer
    self.Attach()
815 89ff748d Thomas Thrainer
    if self.minor is None:
816 89ff748d Thomas Thrainer
      # local device completely unconfigured
817 89ff748d Thomas Thrainer
      self._FastAssemble()
818 89ff748d Thomas Thrainer
    else:
819 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
820 89ff748d Thomas Thrainer
      # the device
821 89ff748d Thomas Thrainer
      self._SlowAssemble()
822 89ff748d Thomas Thrainer
823 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
824 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
825 89ff748d Thomas Thrainer

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

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

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

903 89ff748d Thomas Thrainer
    """
904 89ff748d Thomas Thrainer
    minor = self._aminor
905 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
906 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
907 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
908 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
909 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
910 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
911 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
912 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
913 89ff748d Thomas Thrainer
914 09a78e1c Thomas Thrainer
  def _ShutdownLocal(self, minor):
915 89ff748d Thomas Thrainer
    """Detach from the local device.
916 89ff748d Thomas Thrainer

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

920 47e0abee Thomas Thrainer
    @type minor: int
921 47e0abee Thomas Thrainer
    @param minor: the device to detach from the local device
922 47e0abee Thomas Thrainer

923 89ff748d Thomas Thrainer
    """
924 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenDetachCmd(minor)
925 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
926 89ff748d Thomas Thrainer
    if result.failed:
927 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
928 89ff748d Thomas Thrainer
                      minor, result.output)
929 89ff748d Thomas Thrainer
930 09a78e1c Thomas Thrainer
  def _ShutdownNet(self, minor):
931 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
932 89ff748d Thomas Thrainer

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

935 47e0abee Thomas Thrainer
    @type minor: boolean
936 47e0abee Thomas Thrainer
    @param minor: the device to disconnect from the remote peer
937 47e0abee Thomas Thrainer

938 89ff748d Thomas Thrainer
    """
939 daec28a7 Thomas Thrainer
    family = self._GetNetFamily(minor, self._lhost, self._rhost)
940 daec28a7 Thomas Thrainer
    cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
941 daec28a7 Thomas Thrainer
                                         self._lhost, self._lport,
942 daec28a7 Thomas Thrainer
                                         self._rhost, self._rport)
943 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
944 89ff748d Thomas Thrainer
    if result.failed:
945 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
946 89ff748d Thomas Thrainer
                      minor, result.output)
947 89ff748d Thomas Thrainer
948 89ff748d Thomas Thrainer
  def Shutdown(self):
949 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
950 89ff748d Thomas Thrainer

951 89ff748d Thomas Thrainer
    """
952 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
953 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
954 89ff748d Thomas Thrainer
      return
955 d0d7d7cf Thomas Thrainer
956 d0d7d7cf Thomas Thrainer
    try:
957 d0d7d7cf Thomas Thrainer
      DRBD8.ShutdownAll(self.minor)
958 d0d7d7cf Thomas Thrainer
    finally:
959 d0d7d7cf Thomas Thrainer
      self.minor = None
960 d0d7d7cf Thomas Thrainer
      self.dev_path = None
961 89ff748d Thomas Thrainer
962 89ff748d Thomas Thrainer
  def Remove(self):
963 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
964 89ff748d Thomas Thrainer

965 89ff748d Thomas Thrainer
    """
966 89ff748d Thomas Thrainer
    self.Shutdown()
967 89ff748d Thomas Thrainer
968 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
969 fd300bc7 Thomas Thrainer
    """Rename a device.
970 fd300bc7 Thomas Thrainer

971 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
972 fd300bc7 Thomas Thrainer

973 fd300bc7 Thomas Thrainer
    """
974 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
975 fd300bc7 Thomas Thrainer
976 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
977 47e0abee Thomas Thrainer
    """Resize the DRBD device and its backing storage.
978 47e0abee Thomas Thrainer

979 47e0abee Thomas Thrainer
    See L{BlockDev.Grow} for parameter description.
980 47e0abee Thomas Thrainer

981 47e0abee Thomas Thrainer
    """
982 47e0abee Thomas Thrainer
    if self.minor is None:
983 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
984 47e0abee Thomas Thrainer
    if len(self._children) != 2 or None in self._children:
985 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
986 be9150ea Bernardo Dal Seno
    self._children[0].Grow(amount, dryrun, backingstore, excl_stor)
987 47e0abee Thomas Thrainer
    if dryrun or backingstore:
988 47e0abee Thomas Thrainer
      # DRBD does not support dry-run mode and is not backing storage,
989 47e0abee Thomas Thrainer
      # so we'll return here
990 47e0abee Thomas Thrainer
      return
991 47e0abee Thomas Thrainer
    cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
992 47e0abee Thomas Thrainer
    result = utils.RunCmd(cmd)
993 47e0abee Thomas Thrainer
    if result.failed:
994 47e0abee Thomas Thrainer
      base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
995 47e0abee Thomas Thrainer
996 47e0abee Thomas Thrainer
  @classmethod
997 47e0abee Thomas Thrainer
  def _InitMeta(cls, minor, dev_path):
998 47e0abee Thomas Thrainer
    """Initialize a meta device.
999 47e0abee Thomas Thrainer

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

1002 47e0abee Thomas Thrainer
    @type minor: int
1003 47e0abee Thomas Thrainer
    @param minor: the DRBD minor whose (future) meta device should be
1004 47e0abee Thomas Thrainer
      initialized
1005 47e0abee Thomas Thrainer
    @type dev_path: string
1006 47e0abee Thomas Thrainer
    @param dev_path: path to the meta device to initialize
1007 47e0abee Thomas Thrainer

1008 47e0abee Thomas Thrainer
    """
1009 47e0abee Thomas Thrainer
    # Zero the metadata first, in order to make sure drbdmeta doesn't
1010 47e0abee Thomas Thrainer
    # try to auto-detect existing filesystems or similar (see
1011 47e0abee Thomas Thrainer
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
1012 47e0abee Thomas Thrainer
    # care about the first 128MB of data in the device, even though it
1013 47e0abee Thomas Thrainer
    # can be bigger
1014 47e0abee Thomas Thrainer
    result = utils.RunCmd([constants.DD_CMD,
1015 47e0abee Thomas Thrainer
                           "if=/dev/zero", "of=%s" % dev_path,
1016 47e0abee Thomas Thrainer
                           "bs=1048576", "count=128", "oflag=direct"])
1017 47e0abee Thomas Thrainer
    if result.failed:
1018 47e0abee Thomas Thrainer
      base.ThrowError("Can't wipe the meta device: %s", result.output)
1019 47e0abee Thomas Thrainer
1020 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
1021 47e0abee Thomas Thrainer
    cmd_gen = DRBD8.GetCmdGenerator(info)
1022 47e0abee Thomas Thrainer
    cmd = cmd_gen.GenInitMetaCmd(minor, dev_path)
1023 47e0abee Thomas Thrainer
1024 47e0abee Thomas Thrainer
    result = utils.RunCmd(cmd)
1025 47e0abee Thomas Thrainer
    if result.failed:
1026 47e0abee Thomas Thrainer
      base.ThrowError("Can't initialize meta device: %s", result.output)
1027 47e0abee Thomas Thrainer
1028 89ff748d Thomas Thrainer
  @classmethod
1029 0c3d9c7c Thomas Thrainer
  def Create(cls, unique_id, children, size, spindles, params, excl_stor,
1030 bddc92ee Klaus Aehlig
             dyn_params, *_):
1031 89ff748d Thomas Thrainer
    """Create a new DRBD8 device.
1032 89ff748d Thomas Thrainer

1033 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
1034 89ff748d Thomas Thrainer
    function only initializes the metadata.
1035 89ff748d Thomas Thrainer

1036 89ff748d Thomas Thrainer
    """
1037 89ff748d Thomas Thrainer
    if len(children) != 2:
1038 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("Invalid setup for the drbd device")
1039 89ff748d Thomas Thrainer
    if excl_stor:
1040 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("DRBD device requested with"
1041 89ff748d Thomas Thrainer
                                   " exclusive_storage")
1042 0c3d9c7c Thomas Thrainer
    if constants.DDP_LOCAL_MINOR not in dyn_params:
1043 0c3d9c7c Thomas Thrainer
      raise errors.ProgrammerError("Invalid dynamic params for drbd device %s"
1044 0c3d9c7c Thomas Thrainer
                                   % dyn_params)
1045 89ff748d Thomas Thrainer
    # check that the minor is unused
1046 0c3d9c7c Thomas Thrainer
    aminor = dyn_params[constants.DDP_LOCAL_MINOR]
1047 2fe690f1 Thomas Thrainer
1048 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
1049 47e0abee Thomas Thrainer
    if info.HasMinorStatus(aminor):
1050 47e0abee Thomas Thrainer
      status = info.GetMinorStatus(aminor)
1051 89ff748d Thomas Thrainer
      in_use = status.is_in_use
1052 89ff748d Thomas Thrainer
    else:
1053 89ff748d Thomas Thrainer
      in_use = False
1054 89ff748d Thomas Thrainer
    if in_use:
1055 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: minor is already in use at Create() time",
1056 89ff748d Thomas Thrainer
                      aminor)
1057 89ff748d Thomas Thrainer
    meta = children[1]
1058 89ff748d Thomas Thrainer
    meta.Assemble()
1059 89ff748d Thomas Thrainer
    if not meta.Attach():
1060 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to meta device '%s'",
1061 89ff748d Thomas Thrainer
                      aminor, meta)
1062 89ff748d Thomas Thrainer
    cls._CheckMetaSize(meta.dev_path)
1063 89ff748d Thomas Thrainer
    cls._InitMeta(aminor, meta.dev_path)
1064 0c3d9c7c Thomas Thrainer
    return cls(unique_id, children, size, params, dyn_params)
1065 89ff748d Thomas Thrainer
1066 89ff748d Thomas Thrainer
1067 89ff748d Thomas Thrainer
def _CanReadDevice(path):
1068 89ff748d Thomas Thrainer
  """Check if we can read from the given device.
1069 89ff748d Thomas Thrainer

1070 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
1071 89ff748d Thomas Thrainer

1072 47e0abee Thomas Thrainer
  @type path: string
1073 47e0abee Thomas Thrainer

1074 89ff748d Thomas Thrainer
  """
1075 89ff748d Thomas Thrainer
  try:
1076 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1077 89ff748d Thomas Thrainer
    return True
1078 89ff748d Thomas Thrainer
  except EnvironmentError:
1079 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
1080 89ff748d Thomas Thrainer
    return False