Statistics
| Branch: | Tag: | Revision:

root / lib / block / drbd.py @ 09a78e1c

History | View | Annotate | Download (31.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 89ff748d Thomas Thrainer
from ganeti.block import base
34 27d69b25 Thomas Thrainer
from ganeti.block.drbd_info import DRBD8Info
35 5520d04d Thomas Thrainer
from ganeti.block.drbd_info import DRBD83ShowInfo
36 27c7d9c3 Thomas Thrainer
from ganeti.block.drbd_info import DRBD84ShowInfo
37 09a78e1c Thomas Thrainer
from ganeti.block import drbd_cmdgen
38 89ff748d Thomas Thrainer
39 89ff748d Thomas Thrainer
40 89ff748d Thomas Thrainer
# Size of reads in _CanReadDevice
41 89ff748d Thomas Thrainer
42 89ff748d Thomas Thrainer
_DEVICE_READ_SIZE = 128 * 1024
43 89ff748d Thomas Thrainer
44 89ff748d Thomas Thrainer
45 fd300bc7 Thomas Thrainer
class DRBD8(base.BlockDev):
46 fd300bc7 Thomas Thrainer
  """DRBD v8.x block device.
47 fd300bc7 Thomas Thrainer

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

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

57 89ff748d Thomas Thrainer
  """
58 89ff748d Thomas Thrainer
  _DRBD_MAJOR = 147
59 89ff748d Thomas Thrainer
60 89ff748d Thomas Thrainer
  _USERMODE_HELPER_FILE = "/sys/module/drbd/parameters/usermode_helper"
61 89ff748d Thomas Thrainer
62 fd300bc7 Thomas Thrainer
  _MAX_MINORS = 255
63 fd300bc7 Thomas Thrainer
64 fd300bc7 Thomas Thrainer
  # timeout constants
65 fd300bc7 Thomas Thrainer
  _NET_RECONFIG_TIMEOUT = 60
66 fd300bc7 Thomas Thrainer
67 fd300bc7 Thomas Thrainer
  def __init__(self, unique_id, children, size, params):
68 fd300bc7 Thomas Thrainer
    if children and children.count(None) > 0:
69 fd300bc7 Thomas Thrainer
      children = []
70 fd300bc7 Thomas Thrainer
    if len(children) not in (0, 2):
71 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(children))
72 fd300bc7 Thomas Thrainer
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
73 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(unique_id))
74 fd300bc7 Thomas Thrainer
    (self._lhost, self._lport,
75 fd300bc7 Thomas Thrainer
     self._rhost, self._rport,
76 fd300bc7 Thomas Thrainer
     self._aminor, self._secret) = unique_id
77 fd300bc7 Thomas Thrainer
    if children:
78 fd300bc7 Thomas Thrainer
      if not _CanReadDevice(children[1].dev_path):
79 fd300bc7 Thomas Thrainer
        logging.info("drbd%s: Ignoring unreadable meta device", self._aminor)
80 fd300bc7 Thomas Thrainer
        children = []
81 fd300bc7 Thomas Thrainer
    super(DRBD8, self).__init__(unique_id, children, size, params)
82 fd300bc7 Thomas Thrainer
    self.major = self._DRBD_MAJOR
83 2fe690f1 Thomas Thrainer
84 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
85 2fe690f1 Thomas Thrainer
    version = drbd_info.GetVersion()
86 fd300bc7 Thomas Thrainer
    if version["k_major"] != 8:
87 fd300bc7 Thomas Thrainer
      base.ThrowError("Mismatch in DRBD kernel version and requested ganeti"
88 fd300bc7 Thomas Thrainer
                      " usage: kernel is %s.%s, ganeti wants 8.x",
89 fd300bc7 Thomas Thrainer
                      version["k_major"], version["k_minor"])
90 fd300bc7 Thomas Thrainer
91 27c7d9c3 Thomas Thrainer
    if version["k_minor"] <= 3:
92 27c7d9c3 Thomas Thrainer
      self._show_info_cls = DRBD83ShowInfo
93 09a78e1c Thomas Thrainer
      self._cmd_gen = drbd_cmdgen.DRBD83CmdGenerator(drbd_info)
94 27c7d9c3 Thomas Thrainer
    else:
95 27c7d9c3 Thomas Thrainer
      self._show_info_cls = DRBD84ShowInfo
96 09a78e1c Thomas Thrainer
      # FIXME: use proper command generator!
97 09a78e1c Thomas Thrainer
      self._cmd_gen = None
98 27c7d9c3 Thomas Thrainer
99 fd300bc7 Thomas Thrainer
    if (self._lhost is not None and self._lhost == self._rhost and
100 fd300bc7 Thomas Thrainer
            self._lport == self._rport):
101 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data, same local/remote %s" %
102 fd300bc7 Thomas Thrainer
                       (unique_id,))
103 fd300bc7 Thomas Thrainer
    self.Attach()
104 fd300bc7 Thomas Thrainer
105 89ff748d Thomas Thrainer
  @staticmethod
106 89ff748d Thomas Thrainer
  def GetUsermodeHelper(filename=_USERMODE_HELPER_FILE):
107 89ff748d Thomas Thrainer
    """Returns DRBD usermode_helper currently set.
108 89ff748d Thomas Thrainer

109 89ff748d Thomas Thrainer
    """
110 89ff748d Thomas Thrainer
    try:
111 89ff748d Thomas Thrainer
      helper = utils.ReadFile(filename).splitlines()[0]
112 89ff748d Thomas Thrainer
    except EnvironmentError, err:
113 89ff748d Thomas Thrainer
      if err.errno == errno.ENOENT:
114 89ff748d Thomas Thrainer
        base.ThrowError("The file %s cannot be opened, check if the module"
115 89ff748d Thomas Thrainer
                        " is loaded (%s)", filename, str(err))
116 89ff748d Thomas Thrainer
      else:
117 89ff748d Thomas Thrainer
        base.ThrowError("Can't read DRBD helper file %s: %s",
118 89ff748d Thomas Thrainer
                        filename, str(err))
119 89ff748d Thomas Thrainer
    if not helper:
120 89ff748d Thomas Thrainer
      base.ThrowError("Can't read any data from %s", filename)
121 89ff748d Thomas Thrainer
    return helper
122 89ff748d Thomas Thrainer
123 89ff748d Thomas Thrainer
  @staticmethod
124 89ff748d Thomas Thrainer
  def _DevPath(minor):
125 89ff748d Thomas Thrainer
    """Return the path to a drbd device for a given minor.
126 89ff748d Thomas Thrainer

127 89ff748d Thomas Thrainer
    """
128 89ff748d Thomas Thrainer
    return "/dev/drbd%d" % minor
129 89ff748d Thomas Thrainer
130 89ff748d Thomas Thrainer
  @classmethod
131 89ff748d Thomas Thrainer
  def GetUsedDevs(cls):
132 89ff748d Thomas Thrainer
    """Compute the list of used DRBD devices.
133 89ff748d Thomas Thrainer

134 89ff748d Thomas Thrainer
    """
135 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
136 2fe690f1 Thomas Thrainer
    return filter(lambda m: not drbd_info.GetMinorStatus(m).is_unconfigured,
137 2fe690f1 Thomas Thrainer
                  drbd_info.GetMinors())
138 89ff748d Thomas Thrainer
139 89ff748d Thomas Thrainer
  def _SetFromMinor(self, minor):
140 89ff748d Thomas Thrainer
    """Set our parameters based on the given minor.
141 89ff748d Thomas Thrainer

142 89ff748d Thomas Thrainer
    This sets our minor variable and our dev_path.
143 89ff748d Thomas Thrainer

144 89ff748d Thomas Thrainer
    """
145 89ff748d Thomas Thrainer
    if minor is None:
146 89ff748d Thomas Thrainer
      self.minor = self.dev_path = None
147 89ff748d Thomas Thrainer
      self.attached = False
148 89ff748d Thomas Thrainer
    else:
149 89ff748d Thomas Thrainer
      self.minor = minor
150 89ff748d Thomas Thrainer
      self.dev_path = self._DevPath(minor)
151 89ff748d Thomas Thrainer
      self.attached = True
152 89ff748d Thomas Thrainer
153 89ff748d Thomas Thrainer
  @staticmethod
154 89ff748d Thomas Thrainer
  def _CheckMetaSize(meta_device):
155 89ff748d Thomas Thrainer
    """Check if the given meta device looks like a valid one.
156 89ff748d Thomas Thrainer

157 89ff748d Thomas Thrainer
    This currently only checks the size, which must be around
158 89ff748d Thomas Thrainer
    128MiB.
159 89ff748d Thomas Thrainer

160 89ff748d Thomas Thrainer
    """
161 89ff748d Thomas Thrainer
    result = utils.RunCmd(["blockdev", "--getsize", meta_device])
162 89ff748d Thomas Thrainer
    if result.failed:
163 89ff748d Thomas Thrainer
      base.ThrowError("Failed to get device size: %s - %s",
164 89ff748d Thomas Thrainer
                      result.fail_reason, result.output)
165 89ff748d Thomas Thrainer
    try:
166 89ff748d Thomas Thrainer
      sectors = int(result.stdout)
167 89ff748d Thomas Thrainer
    except (TypeError, ValueError):
168 89ff748d Thomas Thrainer
      base.ThrowError("Invalid output from blockdev: '%s'", result.stdout)
169 89ff748d Thomas Thrainer
    num_bytes = sectors * 512
170 89ff748d Thomas Thrainer
    if num_bytes < 128 * 1024 * 1024: # less than 128MiB
171 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too small (%.2fMib)",
172 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
173 89ff748d Thomas Thrainer
    # the maximum *valid* size of the meta device when living on top
174 89ff748d Thomas Thrainer
    # of LVM is hard to compute: it depends on the number of stripes
175 89ff748d Thomas Thrainer
    # and the PE size; e.g. a 2-stripe, 64MB PE will result in a 128MB
176 89ff748d Thomas Thrainer
    # (normal size), but an eight-stripe 128MB PE will result in a 1GB
177 89ff748d Thomas Thrainer
    # size meta device; as such, we restrict it to 1GB (a little bit
178 89ff748d Thomas Thrainer
    # too generous, but making assumptions about PE size is hard)
179 89ff748d Thomas Thrainer
    if num_bytes > 1024 * 1024 * 1024:
180 89ff748d Thomas Thrainer
      base.ThrowError("Meta device too big (%.2fMiB)",
181 89ff748d Thomas Thrainer
                      (num_bytes / 1024 / 1024))
182 89ff748d Thomas Thrainer
183 89ff748d Thomas Thrainer
  @classmethod
184 89ff748d Thomas Thrainer
  def _InitMeta(cls, minor, dev_path):
185 89ff748d Thomas Thrainer
    """Initialize a meta device.
186 89ff748d Thomas Thrainer

187 89ff748d Thomas Thrainer
    This will not work if the given minor is in use.
188 89ff748d Thomas Thrainer

189 89ff748d Thomas Thrainer
    """
190 89ff748d Thomas Thrainer
    # Zero the metadata first, in order to make sure drbdmeta doesn't
191 89ff748d Thomas Thrainer
    # try to auto-detect existing filesystems or similar (see
192 89ff748d Thomas Thrainer
    # http://code.google.com/p/ganeti/issues/detail?id=182); we only
193 89ff748d Thomas Thrainer
    # care about the first 128MB of data in the device, even though it
194 89ff748d Thomas Thrainer
    # can be bigger
195 89ff748d Thomas Thrainer
    result = utils.RunCmd([constants.DD_CMD,
196 89ff748d Thomas Thrainer
                           "if=/dev/zero", "of=%s" % dev_path,
197 89ff748d Thomas Thrainer
                           "bs=1048576", "count=128", "oflag=direct"])
198 89ff748d Thomas Thrainer
    if result.failed:
199 89ff748d Thomas Thrainer
      base.ThrowError("Can't wipe the meta device: %s", result.output)
200 89ff748d Thomas Thrainer
201 89ff748d Thomas Thrainer
    result = utils.RunCmd(["drbdmeta", "--force", cls._DevPath(minor),
202 89ff748d Thomas Thrainer
                           "v08", dev_path, "0", "create-md"])
203 89ff748d Thomas Thrainer
    if result.failed:
204 89ff748d Thomas Thrainer
      base.ThrowError("Can't initialize meta device: %s", result.output)
205 89ff748d Thomas Thrainer
206 2fe690f1 Thomas Thrainer
  def _FindUnusedMinor(self):
207 89ff748d Thomas Thrainer
    """Find an unused DRBD device.
208 89ff748d Thomas Thrainer

209 89ff748d Thomas Thrainer
    This is specific to 8.x as the minors are allocated dynamically,
210 89ff748d Thomas Thrainer
    so non-existing numbers up to a max minor count are actually free.
211 89ff748d Thomas Thrainer

212 89ff748d Thomas Thrainer
    """
213 89ff748d Thomas Thrainer
214 89ff748d Thomas Thrainer
    highest = None
215 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
216 2fe690f1 Thomas Thrainer
    for minor in drbd_info.GetMinors():
217 2fe690f1 Thomas Thrainer
      status = drbd_info.GetMinorStatus(minor)
218 2fe690f1 Thomas Thrainer
      if not status.is_in_use:
219 2fe690f1 Thomas Thrainer
        return minor
220 2fe690f1 Thomas Thrainer
      highest = max(highest, minor)
221 2fe690f1 Thomas Thrainer
222 89ff748d Thomas Thrainer
    if highest is None: # there are no minors in use at all
223 89ff748d Thomas Thrainer
      return 0
224 2fe690f1 Thomas Thrainer
    if highest >= self._MAX_MINORS:
225 89ff748d Thomas Thrainer
      logging.error("Error: no free drbd minors!")
226 89ff748d Thomas Thrainer
      raise errors.BlockDeviceError("Can't find a free DRBD minor")
227 2fe690f1 Thomas Thrainer
228 89ff748d Thomas Thrainer
    return highest + 1
229 89ff748d Thomas Thrainer
230 09a78e1c Thomas Thrainer
  def _GetShowData(self, minor):
231 89ff748d Thomas Thrainer
    """Return the `drbdsetup show` data for a minor.
232 89ff748d Thomas Thrainer

233 89ff748d Thomas Thrainer
    """
234 09a78e1c Thomas Thrainer
    result = utils.RunCmd(self._cmd_gen.GenShowCmd(minor))
235 89ff748d Thomas Thrainer
    if result.failed:
236 89ff748d Thomas Thrainer
      logging.error("Can't display the drbd config: %s - %s",
237 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
238 89ff748d Thomas Thrainer
      return None
239 89ff748d Thomas Thrainer
    return result.stdout
240 89ff748d Thomas Thrainer
241 27c7d9c3 Thomas Thrainer
  def _GetShowInfo(self, minor):
242 27c7d9c3 Thomas Thrainer
    return self._show_info_cls.GetDevInfo(self._GetShowData(minor))
243 27c7d9c3 Thomas Thrainer
244 89ff748d Thomas Thrainer
  def _MatchesLocal(self, info):
245 89ff748d Thomas Thrainer
    """Test if our local config matches with an existing device.
246 89ff748d Thomas Thrainer

247 89ff748d Thomas Thrainer
    The parameter should be as returned from `_GetDevInfo()`. This
248 89ff748d Thomas Thrainer
    method tests if our local backing device is the same as the one in
249 89ff748d Thomas Thrainer
    the info parameter, in effect testing if we look like the given
250 89ff748d Thomas Thrainer
    device.
251 89ff748d Thomas Thrainer

252 89ff748d Thomas Thrainer
    """
253 89ff748d Thomas Thrainer
    if self._children:
254 89ff748d Thomas Thrainer
      backend, meta = self._children
255 89ff748d Thomas Thrainer
    else:
256 89ff748d Thomas Thrainer
      backend = meta = None
257 89ff748d Thomas Thrainer
258 89ff748d Thomas Thrainer
    if backend is not None:
259 89ff748d Thomas Thrainer
      retval = ("local_dev" in info and info["local_dev"] == backend.dev_path)
260 89ff748d Thomas Thrainer
    else:
261 89ff748d Thomas Thrainer
      retval = ("local_dev" not in info)
262 89ff748d Thomas Thrainer
263 89ff748d Thomas Thrainer
    if meta is not None:
264 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" in info and
265 89ff748d Thomas Thrainer
                           info["meta_dev"] == meta.dev_path)
266 89ff748d Thomas Thrainer
      retval = retval and ("meta_index" in info and
267 89ff748d Thomas Thrainer
                           info["meta_index"] == 0)
268 89ff748d Thomas Thrainer
    else:
269 89ff748d Thomas Thrainer
      retval = retval and ("meta_dev" not in info and
270 89ff748d Thomas Thrainer
                           "meta_index" not in info)
271 89ff748d Thomas Thrainer
    return retval
272 89ff748d Thomas Thrainer
273 89ff748d Thomas Thrainer
  def _MatchesNet(self, info):
274 89ff748d Thomas Thrainer
    """Test if our network config matches with an existing device.
275 89ff748d Thomas Thrainer

276 89ff748d Thomas Thrainer
    The parameter should be as returned from `_GetDevInfo()`. This
277 89ff748d Thomas Thrainer
    method tests if our network configuration is the same as the one
278 89ff748d Thomas Thrainer
    in the info parameter, in effect testing if we look like the given
279 89ff748d Thomas Thrainer
    device.
280 89ff748d Thomas Thrainer

281 89ff748d Thomas Thrainer
    """
282 89ff748d Thomas Thrainer
    if (((self._lhost is None and not ("local_addr" in info)) and
283 89ff748d Thomas Thrainer
         (self._rhost is None and not ("remote_addr" in info)))):
284 89ff748d Thomas Thrainer
      return True
285 89ff748d Thomas Thrainer
286 89ff748d Thomas Thrainer
    if self._lhost is None:
287 89ff748d Thomas Thrainer
      return False
288 89ff748d Thomas Thrainer
289 89ff748d Thomas Thrainer
    if not ("local_addr" in info and
290 89ff748d Thomas Thrainer
            "remote_addr" in info):
291 89ff748d Thomas Thrainer
      return False
292 89ff748d Thomas Thrainer
293 89ff748d Thomas Thrainer
    retval = (info["local_addr"] == (self._lhost, self._lport))
294 89ff748d Thomas Thrainer
    retval = (retval and
295 89ff748d Thomas Thrainer
              info["remote_addr"] == (self._rhost, self._rport))
296 89ff748d Thomas Thrainer
    return retval
297 89ff748d Thomas Thrainer
298 89ff748d Thomas Thrainer
  def _AssembleLocal(self, minor, backend, meta, size):
299 89ff748d Thomas Thrainer
    """Configure the local part of a DRBD device.
300 89ff748d Thomas Thrainer

301 89ff748d Thomas Thrainer
    """
302 09a78e1c Thomas Thrainer
    cmds = self._cmd_gen.GenLocalInitCmds(minor, backend, meta,
303 09a78e1c Thomas Thrainer
                                          size, self.params)
304 89ff748d Thomas Thrainer
305 09a78e1c Thomas Thrainer
    for cmd in cmds:
306 09a78e1c Thomas Thrainer
      result = utils.RunCmd(cmd)
307 09a78e1c Thomas Thrainer
      if result.failed:
308 09a78e1c Thomas Thrainer
        base.ThrowError("drbd%d: can't attach local disk: %s",
309 09a78e1c Thomas Thrainer
                        minor, result.output)
310 89ff748d Thomas Thrainer
311 89ff748d Thomas Thrainer
  def _AssembleNet(self, minor, net_info, protocol,
312 89ff748d Thomas Thrainer
                   dual_pri=False, hmac=None, secret=None):
313 89ff748d Thomas Thrainer
    """Configure the network part of the device.
314 89ff748d Thomas Thrainer

315 89ff748d Thomas Thrainer
    """
316 89ff748d Thomas Thrainer
    lhost, lport, rhost, rport = net_info
317 89ff748d Thomas Thrainer
    if None in net_info:
318 89ff748d Thomas Thrainer
      # we don't want network connection and actually want to make
319 89ff748d Thomas Thrainer
      # sure its shutdown
320 89ff748d Thomas Thrainer
      self._ShutdownNet(minor)
321 89ff748d Thomas Thrainer
      return
322 89ff748d Thomas Thrainer
323 89ff748d Thomas Thrainer
    # Workaround for a race condition. When DRBD is doing its dance to
324 89ff748d Thomas Thrainer
    # establish a connection with its peer, it also sends the
325 89ff748d Thomas Thrainer
    # synchronization speed over the wire. In some cases setting the
326 89ff748d Thomas Thrainer
    # sync speed only after setting up both sides can race with DRBD
327 89ff748d Thomas Thrainer
    # connecting, hence we set it here before telling DRBD anything
328 89ff748d Thomas Thrainer
    # about its peer.
329 89ff748d Thomas Thrainer
    sync_errors = self._SetMinorSyncParams(minor, self.params)
330 89ff748d Thomas Thrainer
    if sync_errors:
331 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
332 89ff748d Thomas Thrainer
                      (minor, utils.CommaJoin(sync_errors)))
333 89ff748d Thomas Thrainer
334 89ff748d Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
335 89ff748d Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
336 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
337 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
338 89ff748d Thomas Thrainer
      family = "ipv6"
339 89ff748d Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
340 89ff748d Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
341 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
342 89ff748d Thomas Thrainer
                        (minor, lhost, rhost))
343 89ff748d Thomas Thrainer
      family = "ipv4"
344 89ff748d Thomas Thrainer
    else:
345 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
346 89ff748d Thomas Thrainer
347 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenNetInitCmd(minor, family, lhost, lport,
348 09a78e1c Thomas Thrainer
                                      rhost, rport, protocol,
349 09a78e1c Thomas Thrainer
                                      dual_pri, hmac, secret, self.params)
350 09a78e1c Thomas Thrainer
351 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
352 89ff748d Thomas Thrainer
    if result.failed:
353 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't setup network: %s - %s",
354 89ff748d Thomas Thrainer
                      minor, result.fail_reason, result.output)
355 89ff748d Thomas Thrainer
356 89ff748d Thomas Thrainer
    def _CheckNetworkConfig():
357 27c7d9c3 Thomas Thrainer
      info = self._GetShowInfo(minor)
358 89ff748d Thomas Thrainer
      if not "local_addr" in info or not "remote_addr" in info:
359 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
360 89ff748d Thomas Thrainer
361 89ff748d Thomas Thrainer
      if (info["local_addr"] != (lhost, lport) or
362 89ff748d Thomas Thrainer
          info["remote_addr"] != (rhost, rport)):
363 89ff748d Thomas Thrainer
        raise utils.RetryAgain()
364 89ff748d Thomas Thrainer
365 89ff748d Thomas Thrainer
    try:
366 89ff748d Thomas Thrainer
      utils.Retry(_CheckNetworkConfig, 1.0, 10.0)
367 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
368 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: timeout while configuring network", minor)
369 89ff748d Thomas Thrainer
370 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
371 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
372 89ff748d Thomas Thrainer

373 89ff748d Thomas Thrainer
    """
374 89ff748d Thomas Thrainer
    if self.minor is None:
375 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
376 89ff748d Thomas Thrainer
                      self._aminor)
377 89ff748d Thomas Thrainer
    if len(devices) != 2:
378 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
379 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
380 89ff748d Thomas Thrainer
    if "local_dev" in info:
381 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
382 89ff748d Thomas Thrainer
    backend, meta = devices
383 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
384 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
385 89ff748d Thomas Thrainer
                      self.minor)
386 89ff748d Thomas Thrainer
    backend.Open()
387 89ff748d Thomas Thrainer
    meta.Open()
388 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
389 89ff748d Thomas Thrainer
    self._InitMeta(self._FindUnusedMinor(), meta.dev_path)
390 89ff748d Thomas Thrainer
391 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
392 89ff748d Thomas Thrainer
    self._children = devices
393 89ff748d Thomas Thrainer
394 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
395 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
396 89ff748d Thomas Thrainer

397 89ff748d Thomas Thrainer
    """
398 89ff748d Thomas Thrainer
    if self.minor is None:
399 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
400 89ff748d Thomas Thrainer
                      self._aminor)
401 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
402 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
403 89ff748d Thomas Thrainer
    if "local_dev" not in info:
404 89ff748d Thomas Thrainer
      return
405 89ff748d Thomas Thrainer
    if len(self._children) != 2:
406 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
407 89ff748d Thomas Thrainer
                      self._children)
408 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
409 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
410 89ff748d Thomas Thrainer
      return
411 89ff748d Thomas Thrainer
    if len(devices) != 2:
412 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
413 89ff748d Thomas Thrainer
                      self.minor)
414 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
415 89ff748d Thomas Thrainer
      if dev != child.dev_path:
416 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
417 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
418 89ff748d Thomas Thrainer
419 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
420 89ff748d Thomas Thrainer
    self._children = []
421 89ff748d Thomas Thrainer
422 2fe690f1 Thomas Thrainer
  def _SetMinorSyncParams(self, minor, params):
423 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
424 89ff748d Thomas Thrainer

425 89ff748d Thomas Thrainer
    This is the low-level implementation.
426 89ff748d Thomas Thrainer

427 89ff748d Thomas Thrainer
    @type minor: int
428 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
429 89ff748d Thomas Thrainer
    @type params: dict
430 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
431 89ff748d Thomas Thrainer
    @rtype: list
432 89ff748d Thomas Thrainer
    @return: a list of error messages
433 89ff748d Thomas Thrainer

434 89ff748d Thomas Thrainer
    """
435 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
436 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
437 89ff748d Thomas Thrainer
    if result.failed:
438 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
439 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
440 89ff748d Thomas Thrainer
      logging.error(msg)
441 89ff748d Thomas Thrainer
      return [msg]
442 89ff748d Thomas Thrainer
443 89ff748d Thomas Thrainer
    return []
444 89ff748d Thomas Thrainer
445 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
446 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
447 89ff748d Thomas Thrainer

448 89ff748d Thomas Thrainer
    @type params: dict
449 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
450 89ff748d Thomas Thrainer
    @rtype: list
451 89ff748d Thomas Thrainer
    @return: a list of error messages, emitted both by the current node and by
452 89ff748d Thomas Thrainer
    children. An empty list means no errors
453 89ff748d Thomas Thrainer

454 89ff748d Thomas Thrainer
    """
455 89ff748d Thomas Thrainer
    if self.minor is None:
456 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
457 89ff748d Thomas Thrainer
      logging.info(err)
458 89ff748d Thomas Thrainer
      return [err]
459 89ff748d Thomas Thrainer
460 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).SetSyncParams(params)
461 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
462 89ff748d Thomas Thrainer
    return children_result
463 89ff748d Thomas Thrainer
464 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
465 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
466 89ff748d Thomas Thrainer

467 89ff748d Thomas Thrainer
    @param pause: Wether to pause or resume
468 89ff748d Thomas Thrainer
    @return: the success of the operation
469 89ff748d Thomas Thrainer

470 89ff748d Thomas Thrainer
    """
471 89ff748d Thomas Thrainer
    if self.minor is None:
472 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
473 89ff748d Thomas Thrainer
      return False
474 89ff748d Thomas Thrainer
475 89ff748d Thomas Thrainer
    children_result = super(DRBD8, self).PauseResumeSync(pause)
476 89ff748d Thomas Thrainer
477 89ff748d Thomas Thrainer
    if pause:
478 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
479 89ff748d Thomas Thrainer
    else:
480 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
481 89ff748d Thomas Thrainer
482 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
483 89ff748d Thomas Thrainer
    if result.failed:
484 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
485 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
486 89ff748d Thomas Thrainer
    return not result.failed and children_result
487 89ff748d Thomas Thrainer
488 89ff748d Thomas Thrainer
  def GetProcStatus(self):
489 89ff748d Thomas Thrainer
    """Return device data from /proc.
490 89ff748d Thomas Thrainer

491 89ff748d Thomas Thrainer
    """
492 89ff748d Thomas Thrainer
    if self.minor is None:
493 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
494 89ff748d Thomas Thrainer
                      self._aminor)
495 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
496 2fe690f1 Thomas Thrainer
    if not drbd_info.HasMinorStatus(self.minor):
497 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
498 2fe690f1 Thomas Thrainer
    return drbd_info.GetMinorStatus(self.minor)
499 89ff748d Thomas Thrainer
500 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
501 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
502 89ff748d Thomas Thrainer

503 89ff748d Thomas Thrainer

504 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
505 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
506 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
507 89ff748d Thomas Thrainer

508 89ff748d Thomas Thrainer

509 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
510 89ff748d Thomas Thrainer
    network not connected or local disk missing.
511 89ff748d Thomas Thrainer

512 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
513 89ff748d Thomas Thrainer
    disk or not.
514 89ff748d Thomas Thrainer

515 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
516 89ff748d Thomas Thrainer

517 89ff748d Thomas Thrainer
    """
518 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
519 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
520 89ff748d Thomas Thrainer
521 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
522 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
523 89ff748d Thomas Thrainer
524 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
525 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
526 89ff748d Thomas Thrainer
    elif stats.is_diskless:
527 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
528 89ff748d Thomas Thrainer
    else:
529 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
530 89ff748d Thomas Thrainer
531 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
532 89ff748d Thomas Thrainer
                                  major=self.major,
533 89ff748d Thomas Thrainer
                                  minor=self.minor,
534 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
535 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
536 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
537 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
538 89ff748d Thomas Thrainer
539 89ff748d Thomas Thrainer
  def Open(self, force=False):
540 89ff748d Thomas Thrainer
    """Make the local state primary.
541 89ff748d Thomas Thrainer

542 89ff748d Thomas Thrainer
    If the 'force' parameter is given, the '-o' option is passed to
543 89ff748d Thomas Thrainer
    drbdsetup. Since this is a potentially dangerous operation, the
544 89ff748d Thomas Thrainer
    force flag should be only given after creation, when it actually
545 89ff748d Thomas Thrainer
    is mandatory.
546 89ff748d Thomas Thrainer

547 89ff748d Thomas Thrainer
    """
548 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
549 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
550 89ff748d Thomas Thrainer
      return False
551 09a78e1c Thomas Thrainer
552 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
553 09a78e1c Thomas Thrainer
554 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
555 89ff748d Thomas Thrainer
    if result.failed:
556 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
557 89ff748d Thomas Thrainer
                      result.output)
558 89ff748d Thomas Thrainer
559 89ff748d Thomas Thrainer
  def Close(self):
560 89ff748d Thomas Thrainer
    """Make the local state secondary.
561 89ff748d Thomas Thrainer

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

564 89ff748d Thomas Thrainer
    """
565 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
566 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
567 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
568 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
569 89ff748d Thomas Thrainer
    if result.failed:
570 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
571 89ff748d Thomas Thrainer
                      self.minor, result.output)
572 89ff748d Thomas Thrainer
573 89ff748d Thomas Thrainer
  def DisconnectNet(self):
574 89ff748d Thomas Thrainer
    """Removes network configuration.
575 89ff748d Thomas Thrainer

576 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
577 89ff748d Thomas Thrainer

578 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
579 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
580 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
581 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
582 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
583 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
584 89ff748d Thomas Thrainer
    exception.
585 89ff748d Thomas Thrainer

586 89ff748d Thomas Thrainer
    """
587 89ff748d Thomas Thrainer
    if self.minor is None:
588 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: disk not attached in re-attach net",
589 89ff748d Thomas Thrainer
                      self._aminor)
590 89ff748d Thomas Thrainer
591 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
592 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: DRBD disk missing network info in"
593 89ff748d Thomas Thrainer
                      " DisconnectNet()", self.minor)
594 89ff748d Thomas Thrainer
595 89ff748d Thomas Thrainer
    class _DisconnectStatus:
596 89ff748d Thomas Thrainer
      def __init__(self, ever_disconnected):
597 89ff748d Thomas Thrainer
        self.ever_disconnected = ever_disconnected
598 89ff748d Thomas Thrainer
599 89ff748d Thomas Thrainer
    dstatus = _DisconnectStatus(base.IgnoreError(self._ShutdownNet, self.minor))
600 89ff748d Thomas Thrainer
601 89ff748d Thomas Thrainer
    def _WaitForDisconnect():
602 89ff748d Thomas Thrainer
      if self.GetProcStatus().is_standalone:
603 89ff748d Thomas Thrainer
        return
604 89ff748d Thomas Thrainer
605 89ff748d Thomas Thrainer
      # retry the disconnect, it seems possible that due to a well-time
606 89ff748d Thomas Thrainer
      # disconnect on the peer, my disconnect command might be ignored and
607 89ff748d Thomas Thrainer
      # forgotten
608 89ff748d Thomas Thrainer
      dstatus.ever_disconnected = \
609 89ff748d Thomas Thrainer
        base.IgnoreError(self._ShutdownNet, self.minor) or \
610 89ff748d Thomas Thrainer
        dstatus.ever_disconnected
611 89ff748d Thomas Thrainer
612 89ff748d Thomas Thrainer
      raise utils.RetryAgain()
613 89ff748d Thomas Thrainer
614 89ff748d Thomas Thrainer
    # Keep start time
615 89ff748d Thomas Thrainer
    start_time = time.time()
616 89ff748d Thomas Thrainer
617 89ff748d Thomas Thrainer
    try:
618 89ff748d Thomas Thrainer
      # Start delay at 100 milliseconds and grow up to 2 seconds
619 89ff748d Thomas Thrainer
      utils.Retry(_WaitForDisconnect, (0.1, 1.5, 2.0),
620 89ff748d Thomas Thrainer
                  self._NET_RECONFIG_TIMEOUT)
621 89ff748d Thomas Thrainer
    except utils.RetryTimeout:
622 89ff748d Thomas Thrainer
      if dstatus.ever_disconnected:
623 89ff748d Thomas Thrainer
        msg = ("drbd%d: device did not react to the"
624 89ff748d Thomas Thrainer
               " 'disconnect' command in a timely manner")
625 89ff748d Thomas Thrainer
      else:
626 89ff748d Thomas Thrainer
        msg = "drbd%d: can't shutdown network, even after multiple retries"
627 89ff748d Thomas Thrainer
628 89ff748d Thomas Thrainer
      base.ThrowError(msg, self.minor)
629 89ff748d Thomas Thrainer
630 89ff748d Thomas Thrainer
    reconfig_time = time.time() - start_time
631 89ff748d Thomas Thrainer
    if reconfig_time > (self._NET_RECONFIG_TIMEOUT * 0.25):
632 89ff748d Thomas Thrainer
      logging.info("drbd%d: DisconnectNet: detach took %.3f seconds",
633 89ff748d Thomas Thrainer
                   self.minor, reconfig_time)
634 89ff748d Thomas Thrainer
635 89ff748d Thomas Thrainer
  def AttachNet(self, multimaster):
636 89ff748d Thomas Thrainer
    """Reconnects the network.
637 89ff748d Thomas Thrainer

638 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
639 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
640 89ff748d Thomas Thrainer
    but have valid network configuration data.
641 89ff748d Thomas Thrainer

642 89ff748d Thomas Thrainer
    Args:
643 89ff748d Thomas Thrainer
      - multimaster: init the network in dual-primary mode
644 89ff748d Thomas Thrainer

645 89ff748d Thomas Thrainer
    """
646 89ff748d Thomas Thrainer
    if self.minor is None:
647 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
648 89ff748d Thomas Thrainer
649 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
650 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
651 89ff748d Thomas Thrainer
652 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
653 89ff748d Thomas Thrainer
654 89ff748d Thomas Thrainer
    if not status.is_standalone:
655 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
656 89ff748d Thomas Thrainer
                      self.minor)
657 89ff748d Thomas Thrainer
658 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
659 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
660 89ff748d Thomas Thrainer
                      constants.DRBD_NET_PROTOCOL, dual_pri=multimaster,
661 89ff748d Thomas Thrainer
                      hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
662 89ff748d Thomas Thrainer
663 89ff748d Thomas Thrainer
  def Attach(self):
664 89ff748d Thomas Thrainer
    """Check if our minor is configured.
665 89ff748d Thomas Thrainer

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

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

673 89ff748d Thomas Thrainer
    """
674 89ff748d Thomas Thrainer
    used_devs = self.GetUsedDevs()
675 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
676 89ff748d Thomas Thrainer
      minor = self._aminor
677 89ff748d Thomas Thrainer
    else:
678 89ff748d Thomas Thrainer
      minor = None
679 89ff748d Thomas Thrainer
680 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
681 89ff748d Thomas Thrainer
    return minor is not None
682 89ff748d Thomas Thrainer
683 89ff748d Thomas Thrainer
  def Assemble(self):
684 89ff748d Thomas Thrainer
    """Assemble the drbd.
685 89ff748d Thomas Thrainer

686 89ff748d Thomas Thrainer
    Method:
687 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
688 89ff748d Thomas Thrainer
        our config
689 89ff748d Thomas Thrainer
      - if not, we create it from zero
690 89ff748d Thomas Thrainer
      - anyway, set the device parameters
691 89ff748d Thomas Thrainer

692 89ff748d Thomas Thrainer
    """
693 89ff748d Thomas Thrainer
    super(DRBD8, self).Assemble()
694 89ff748d Thomas Thrainer
695 89ff748d Thomas Thrainer
    self.Attach()
696 89ff748d Thomas Thrainer
    if self.minor is None:
697 89ff748d Thomas Thrainer
      # local device completely unconfigured
698 89ff748d Thomas Thrainer
      self._FastAssemble()
699 89ff748d Thomas Thrainer
    else:
700 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
701 89ff748d Thomas Thrainer
      # the device
702 89ff748d Thomas Thrainer
      self._SlowAssemble()
703 89ff748d Thomas Thrainer
704 89ff748d Thomas Thrainer
    sync_errors = self.SetSyncParams(self.params)
705 89ff748d Thomas Thrainer
    if sync_errors:
706 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
707 89ff748d Thomas Thrainer
                      (self.minor, utils.CommaJoin(sync_errors)))
708 89ff748d Thomas Thrainer
709 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
710 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
711 89ff748d Thomas Thrainer

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

716 89ff748d Thomas Thrainer
    """
717 89ff748d Thomas Thrainer
    # TODO: Rewrite to not use a for loop just because there is 'break'
718 89ff748d Thomas Thrainer
    # pylint: disable=W0631
719 89ff748d Thomas Thrainer
    net_data = (self._lhost, self._lport, self._rhost, self._rport)
720 89ff748d Thomas Thrainer
    for minor in (self._aminor,):
721 27c7d9c3 Thomas Thrainer
      info = self._GetShowInfo(minor)
722 89ff748d Thomas Thrainer
      match_l = self._MatchesLocal(info)
723 89ff748d Thomas Thrainer
      match_r = self._MatchesNet(info)
724 89ff748d Thomas Thrainer
725 89ff748d Thomas Thrainer
      if match_l and match_r:
726 89ff748d Thomas Thrainer
        # everything matches
727 89ff748d Thomas Thrainer
        break
728 89ff748d Thomas Thrainer
729 89ff748d Thomas Thrainer
      if match_l and not match_r and "local_addr" not in info:
730 89ff748d Thomas Thrainer
        # disk matches, but not attached to network, attach and recheck
731 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
732 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
733 27c7d9c3 Thomas Thrainer
        if self._MatchesNet(self._GetShowInfo(minor)):
734 89ff748d Thomas Thrainer
          break
735 89ff748d Thomas Thrainer
        else:
736 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
737 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
738 89ff748d Thomas Thrainer
739 89ff748d Thomas Thrainer
      if match_r and "local_dev" not in info:
740 89ff748d Thomas Thrainer
        # no local disk, but network attached and it matches
741 89ff748d Thomas Thrainer
        self._AssembleLocal(minor, self._children[0].dev_path,
742 89ff748d Thomas Thrainer
                            self._children[1].dev_path, self.size)
743 27c7d9c3 Thomas Thrainer
        if self._MatchesNet(self._GetShowInfo(minor)):
744 89ff748d Thomas Thrainer
          break
745 89ff748d Thomas Thrainer
        else:
746 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: disk attach successful, but 'drbdsetup"
747 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
748 89ff748d Thomas Thrainer
749 89ff748d Thomas Thrainer
      # this case must be considered only if we actually have local
750 89ff748d Thomas Thrainer
      # storage, i.e. not in diskless mode, because all diskless
751 89ff748d Thomas Thrainer
      # devices are equal from the point of view of local
752 89ff748d Thomas Thrainer
      # configuration
753 89ff748d Thomas Thrainer
      if (match_l and "local_dev" in info and
754 89ff748d Thomas Thrainer
          not match_r and "local_addr" in info):
755 89ff748d Thomas Thrainer
        # strange case - the device network part points to somewhere
756 89ff748d Thomas Thrainer
        # else, even though its local storage is ours; as we own the
757 89ff748d Thomas Thrainer
        # drbd space, we try to disconnect from the remote peer and
758 89ff748d Thomas Thrainer
        # reconnect to our correct one
759 89ff748d Thomas Thrainer
        try:
760 89ff748d Thomas Thrainer
          self._ShutdownNet(minor)
761 89ff748d Thomas Thrainer
        except errors.BlockDeviceError, err:
762 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: device has correct local storage, wrong"
763 89ff748d Thomas Thrainer
                          " remote peer and is unable to disconnect in order"
764 89ff748d Thomas Thrainer
                          " to attach to the correct peer: %s", minor, str(err))
765 89ff748d Thomas Thrainer
        # note: _AssembleNet also handles the case when we don't want
766 89ff748d Thomas Thrainer
        # local storage (i.e. one or more of the _[lr](host|port) is
767 89ff748d Thomas Thrainer
        # None)
768 89ff748d Thomas Thrainer
        self._AssembleNet(minor, net_data, constants.DRBD_NET_PROTOCOL,
769 89ff748d Thomas Thrainer
                          hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
770 27c7d9c3 Thomas Thrainer
        if self._MatchesNet(self._GetShowInfo(minor)):
771 89ff748d Thomas Thrainer
          break
772 89ff748d Thomas Thrainer
        else:
773 89ff748d Thomas Thrainer
          base.ThrowError("drbd%d: network attach successful, but 'drbdsetup"
774 89ff748d Thomas Thrainer
                          " show' disagrees", minor)
775 89ff748d Thomas Thrainer
776 89ff748d Thomas Thrainer
    else:
777 89ff748d Thomas Thrainer
      minor = None
778 89ff748d Thomas Thrainer
779 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
780 89ff748d Thomas Thrainer
    if minor is None:
781 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot activate, unknown or unhandled reason",
782 89ff748d Thomas Thrainer
                      self._aminor)
783 89ff748d Thomas Thrainer
784 89ff748d Thomas Thrainer
  def _FastAssemble(self):
785 89ff748d Thomas Thrainer
    """Assemble the drbd device from zero.
786 89ff748d Thomas Thrainer

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

789 89ff748d Thomas Thrainer
    """
790 89ff748d Thomas Thrainer
    minor = self._aminor
791 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
792 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
793 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
794 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
795 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
796 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
797 89ff748d Thomas Thrainer
                        constants.DRBD_NET_PROTOCOL,
798 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
799 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
800 89ff748d Thomas Thrainer
801 09a78e1c Thomas Thrainer
  def _ShutdownLocal(self, minor):
802 89ff748d Thomas Thrainer
    """Detach from the local device.
803 89ff748d Thomas Thrainer

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

807 89ff748d Thomas Thrainer
    """
808 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenDetachCmd(minor)
809 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
810 89ff748d Thomas Thrainer
    if result.failed:
811 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
812 89ff748d Thomas Thrainer
                      minor, result.output)
813 89ff748d Thomas Thrainer
814 09a78e1c Thomas Thrainer
  def _ShutdownNet(self, minor):
815 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
816 89ff748d Thomas Thrainer

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

819 89ff748d Thomas Thrainer
    """
820 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenDisconnectCmd(minor)
821 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
822 89ff748d Thomas Thrainer
    if result.failed:
823 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
824 89ff748d Thomas Thrainer
                      minor, result.output)
825 89ff748d Thomas Thrainer
826 89ff748d Thomas Thrainer
  @classmethod
827 89ff748d Thomas Thrainer
  def _ShutdownAll(cls, minor):
828 89ff748d Thomas Thrainer
    """Deactivate the device.
829 89ff748d Thomas Thrainer

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

832 89ff748d Thomas Thrainer
    """
833 09a78e1c Thomas Thrainer
    # FIXME: _ShutdownAll, despite being private, is used in nodemaint.py.
834 09a78e1c Thomas Thrainer
    # That's why we can't make it an instance method, which in turn requires
835 09a78e1c Thomas Thrainer
    # us to duplicate code here (from __init__). This should be proberly fixed.
836 09a78e1c Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
837 09a78e1c Thomas Thrainer
    if drbd_info.GetVersion()["k_minor"] <= 3:
838 09a78e1c Thomas Thrainer
      cmd_gen = drbd_cmdgen.DRBD83CmdGenerator(drbd_info)
839 09a78e1c Thomas Thrainer
    else:
840 09a78e1c Thomas Thrainer
      # FIXME: use proper command generator!
841 09a78e1c Thomas Thrainer
      cmd_gen = None
842 09a78e1c Thomas Thrainer
843 09a78e1c Thomas Thrainer
    cmd = cmd_gen.GenDownCmd(minor)
844 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
845 89ff748d Thomas Thrainer
    if result.failed:
846 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown drbd device: %s",
847 89ff748d Thomas Thrainer
                      minor, result.output)
848 89ff748d Thomas Thrainer
849 89ff748d Thomas Thrainer
  def Shutdown(self):
850 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
851 89ff748d Thomas Thrainer

852 89ff748d Thomas Thrainer
    """
853 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
854 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
855 89ff748d Thomas Thrainer
      return
856 89ff748d Thomas Thrainer
    minor = self.minor
857 89ff748d Thomas Thrainer
    self.minor = None
858 89ff748d Thomas Thrainer
    self.dev_path = None
859 89ff748d Thomas Thrainer
    self._ShutdownAll(minor)
860 89ff748d Thomas Thrainer
861 89ff748d Thomas Thrainer
  def Remove(self):
862 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
863 89ff748d Thomas Thrainer

864 89ff748d Thomas Thrainer
    """
865 89ff748d Thomas Thrainer
    self.Shutdown()
866 89ff748d Thomas Thrainer
867 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
868 fd300bc7 Thomas Thrainer
    """Rename a device.
869 fd300bc7 Thomas Thrainer

870 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
871 fd300bc7 Thomas Thrainer

872 fd300bc7 Thomas Thrainer
    """
873 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
874 fd300bc7 Thomas Thrainer
875 89ff748d Thomas Thrainer
  @classmethod
876 89ff748d Thomas Thrainer
  def Create(cls, unique_id, children, size, params, excl_stor):
877 89ff748d Thomas Thrainer
    """Create a new DRBD8 device.
878 89ff748d Thomas Thrainer

879 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
880 89ff748d Thomas Thrainer
    function only initializes the metadata.
881 89ff748d Thomas Thrainer

882 89ff748d Thomas Thrainer
    """
883 89ff748d Thomas Thrainer
    if len(children) != 2:
884 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("Invalid setup for the drbd device")
885 89ff748d Thomas Thrainer
    if excl_stor:
886 89ff748d Thomas Thrainer
      raise errors.ProgrammerError("DRBD device requested with"
887 89ff748d Thomas Thrainer
                                   " exclusive_storage")
888 89ff748d Thomas Thrainer
    # check that the minor is unused
889 89ff748d Thomas Thrainer
    aminor = unique_id[4]
890 2fe690f1 Thomas Thrainer
891 2fe690f1 Thomas Thrainer
    drbd_info = DRBD8Info.CreateFromFile()
892 2fe690f1 Thomas Thrainer
    if drbd_info.HasMinorStatus(aminor):
893 2fe690f1 Thomas Thrainer
      status = drbd_info.GetMinorStatus(aminor)
894 89ff748d Thomas Thrainer
      in_use = status.is_in_use
895 89ff748d Thomas Thrainer
    else:
896 89ff748d Thomas Thrainer
      in_use = False
897 89ff748d Thomas Thrainer
    if in_use:
898 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: minor is already in use at Create() time",
899 89ff748d Thomas Thrainer
                      aminor)
900 89ff748d Thomas Thrainer
    meta = children[1]
901 89ff748d Thomas Thrainer
    meta.Assemble()
902 89ff748d Thomas Thrainer
    if not meta.Attach():
903 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to meta device '%s'",
904 89ff748d Thomas Thrainer
                      aminor, meta)
905 89ff748d Thomas Thrainer
    cls._CheckMetaSize(meta.dev_path)
906 89ff748d Thomas Thrainer
    cls._InitMeta(aminor, meta.dev_path)
907 89ff748d Thomas Thrainer
    return cls(unique_id, children, size, params)
908 89ff748d Thomas Thrainer
909 89ff748d Thomas Thrainer
  def Grow(self, amount, dryrun, backingstore):
910 89ff748d Thomas Thrainer
    """Resize the DRBD device and its backing storage.
911 89ff748d Thomas Thrainer

912 89ff748d Thomas Thrainer
    """
913 89ff748d Thomas Thrainer
    if self.minor is None:
914 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: Grow called while not attached", self._aminor)
915 89ff748d Thomas Thrainer
    if len(self._children) != 2 or None in self._children:
916 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: cannot grow diskless device", self.minor)
917 89ff748d Thomas Thrainer
    self._children[0].Grow(amount, dryrun, backingstore)
918 89ff748d Thomas Thrainer
    if dryrun or backingstore:
919 89ff748d Thomas Thrainer
      # DRBD does not support dry-run mode and is not backing storage,
920 89ff748d Thomas Thrainer
      # so we'll return here
921 89ff748d Thomas Thrainer
      return
922 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenResizeCmd(self.minor, self.size + amount)
923 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
924 89ff748d Thomas Thrainer
    if result.failed:
925 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: resize failed: %s", self.minor, result.output)
926 89ff748d Thomas Thrainer
927 89ff748d Thomas Thrainer
928 89ff748d Thomas Thrainer
def _CanReadDevice(path):
929 89ff748d Thomas Thrainer
  """Check if we can read from the given device.
930 89ff748d Thomas Thrainer

931 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
932 89ff748d Thomas Thrainer

933 89ff748d Thomas Thrainer
  """
934 89ff748d Thomas Thrainer
  try:
935 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
936 89ff748d Thomas Thrainer
    return True
937 89ff748d Thomas Thrainer
  except EnvironmentError:
938 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
939 89ff748d Thomas Thrainer
    return False