Statistics
| Branch: | Tag: | Revision:

root / lib / storage / drbd.py @ 178ad717

History | View | Annotate | Download (35.4 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 0c3d9c7c Thomas Thrainer
  def __init__(self, unique_id, children, size, params, dyn_params):
178 fd300bc7 Thomas Thrainer
    if children and children.count(None) > 0:
179 fd300bc7 Thomas Thrainer
      children = []
180 fd300bc7 Thomas Thrainer
    if len(children) not in (0, 2):
181 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(children))
182 fd300bc7 Thomas Thrainer
    if not isinstance(unique_id, (tuple, list)) or len(unique_id) != 6:
183 fd300bc7 Thomas Thrainer
      raise ValueError("Invalid configuration data %s" % str(unique_id))
184 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 0c3d9c7c Thomas Thrainer
                                   dyn_params)
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 daec28a7 Thomas Thrainer
  @staticmethod
457 daec28a7 Thomas Thrainer
  def _GetNetFamily(minor, lhost, rhost):
458 daec28a7 Thomas Thrainer
    if netutils.IP6Address.IsValid(lhost):
459 daec28a7 Thomas Thrainer
      if not netutils.IP6Address.IsValid(rhost):
460 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
461 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
462 daec28a7 Thomas Thrainer
      return "ipv6"
463 daec28a7 Thomas Thrainer
    elif netutils.IP4Address.IsValid(lhost):
464 daec28a7 Thomas Thrainer
      if not netutils.IP4Address.IsValid(rhost):
465 daec28a7 Thomas Thrainer
        base.ThrowError("drbd%d: can't connect ip %s to ip %s" %
466 daec28a7 Thomas Thrainer
                        (minor, lhost, rhost))
467 daec28a7 Thomas Thrainer
      return "ipv4"
468 daec28a7 Thomas Thrainer
    else:
469 daec28a7 Thomas Thrainer
      base.ThrowError("drbd%d: Invalid ip %s" % (minor, lhost))
470 daec28a7 Thomas Thrainer
471 89ff748d Thomas Thrainer
  def AddChildren(self, devices):
472 89ff748d Thomas Thrainer
    """Add a disk to the DRBD device.
473 89ff748d Thomas Thrainer

474 47e0abee Thomas Thrainer
    @type devices: list of L{BlockDev}
475 47e0abee Thomas Thrainer
    @param devices: a list of exactly two L{BlockDev} objects; the first
476 47e0abee Thomas Thrainer
      denotes the data device, the second the meta device for this DRBD device
477 47e0abee Thomas Thrainer

478 89ff748d Thomas Thrainer
    """
479 89ff748d Thomas Thrainer
    if self.minor is None:
480 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to dbrd8 during AddChildren",
481 89ff748d Thomas Thrainer
                      self._aminor)
482 89ff748d Thomas Thrainer
    if len(devices) != 2:
483 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: need two devices for AddChildren", self.minor)
484 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
485 89ff748d Thomas Thrainer
    if "local_dev" in info:
486 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: already attached to a local disk", self.minor)
487 89ff748d Thomas Thrainer
    backend, meta = devices
488 89ff748d Thomas Thrainer
    if backend.dev_path is None or meta.dev_path is None:
489 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: children not ready during AddChildren",
490 89ff748d Thomas Thrainer
                      self.minor)
491 89ff748d Thomas Thrainer
    backend.Open()
492 89ff748d Thomas Thrainer
    meta.Open()
493 89ff748d Thomas Thrainer
    self._CheckMetaSize(meta.dev_path)
494 47e0abee Thomas Thrainer
    self._InitMeta(DRBD8.FindUnusedMinor(), meta.dev_path)
495 89ff748d Thomas Thrainer
496 89ff748d Thomas Thrainer
    self._AssembleLocal(self.minor, backend.dev_path, meta.dev_path, self.size)
497 89ff748d Thomas Thrainer
    self._children = devices
498 89ff748d Thomas Thrainer
499 89ff748d Thomas Thrainer
  def RemoveChildren(self, devices):
500 89ff748d Thomas Thrainer
    """Detach the drbd device from local storage.
501 89ff748d Thomas Thrainer

502 47e0abee Thomas Thrainer
    @type devices: list of L{BlockDev}
503 47e0abee Thomas Thrainer
    @param devices: a list of exactly two L{BlockDev} objects; the first
504 47e0abee Thomas Thrainer
      denotes the data device, the second the meta device for this DRBD device
505 47e0abee Thomas Thrainer

506 89ff748d Thomas Thrainer
    """
507 89ff748d Thomas Thrainer
    if self.minor is None:
508 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't attach to drbd8 during RemoveChildren",
509 89ff748d Thomas Thrainer
                      self._aminor)
510 89ff748d Thomas Thrainer
    # early return if we don't actually have backing storage
511 27c7d9c3 Thomas Thrainer
    info = self._GetShowInfo(self.minor)
512 89ff748d Thomas Thrainer
    if "local_dev" not in info:
513 89ff748d Thomas Thrainer
      return
514 89ff748d Thomas Thrainer
    if len(self._children) != 2:
515 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we don't have two children: %s", self.minor,
516 89ff748d Thomas Thrainer
                      self._children)
517 89ff748d Thomas Thrainer
    if self._children.count(None) == 2: # we don't actually have children :)
518 89ff748d Thomas Thrainer
      logging.warning("drbd%d: requested detach while detached", self.minor)
519 89ff748d Thomas Thrainer
      return
520 89ff748d Thomas Thrainer
    if len(devices) != 2:
521 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: we need two children in RemoveChildren",
522 89ff748d Thomas Thrainer
                      self.minor)
523 89ff748d Thomas Thrainer
    for child, dev in zip(self._children, devices):
524 89ff748d Thomas Thrainer
      if dev != child.dev_path:
525 89ff748d Thomas Thrainer
        base.ThrowError("drbd%d: mismatch in local storage (%s != %s) in"
526 89ff748d Thomas Thrainer
                        " RemoveChildren", self.minor, dev, child.dev_path)
527 89ff748d Thomas Thrainer
528 89ff748d Thomas Thrainer
    self._ShutdownLocal(self.minor)
529 89ff748d Thomas Thrainer
    self._children = []
530 89ff748d Thomas Thrainer
531 2fe690f1 Thomas Thrainer
  def _SetMinorSyncParams(self, minor, params):
532 89ff748d Thomas Thrainer
    """Set the parameters of the DRBD syncer.
533 89ff748d Thomas Thrainer

534 89ff748d Thomas Thrainer
    This is the low-level implementation.
535 89ff748d Thomas Thrainer

536 89ff748d Thomas Thrainer
    @type minor: int
537 89ff748d Thomas Thrainer
    @param minor: the drbd minor whose settings we change
538 89ff748d Thomas Thrainer
    @type params: dict
539 89ff748d Thomas Thrainer
    @param params: LD level disk parameters related to the synchronization
540 89ff748d Thomas Thrainer
    @rtype: list
541 89ff748d Thomas Thrainer
    @return: a list of error messages
542 89ff748d Thomas Thrainer

543 89ff748d Thomas Thrainer
    """
544 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSyncParamsCmd(minor, params)
545 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
546 89ff748d Thomas Thrainer
    if result.failed:
547 89ff748d Thomas Thrainer
      msg = ("Can't change syncer rate: %s - %s" %
548 89ff748d Thomas Thrainer
             (result.fail_reason, result.output))
549 89ff748d Thomas Thrainer
      logging.error(msg)
550 89ff748d Thomas Thrainer
      return [msg]
551 89ff748d Thomas Thrainer
552 89ff748d Thomas Thrainer
    return []
553 89ff748d Thomas Thrainer
554 89ff748d Thomas Thrainer
  def SetSyncParams(self, params):
555 89ff748d Thomas Thrainer
    """Set the synchronization parameters of the DRBD syncer.
556 89ff748d Thomas Thrainer

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

559 89ff748d Thomas Thrainer
    """
560 89ff748d Thomas Thrainer
    if self.minor is None:
561 89ff748d Thomas Thrainer
      err = "Not attached during SetSyncParams"
562 89ff748d Thomas Thrainer
      logging.info(err)
563 89ff748d Thomas Thrainer
      return [err]
564 89ff748d Thomas Thrainer
565 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).SetSyncParams(params)
566 89ff748d Thomas Thrainer
    children_result.extend(self._SetMinorSyncParams(self.minor, params))
567 89ff748d Thomas Thrainer
    return children_result
568 89ff748d Thomas Thrainer
569 89ff748d Thomas Thrainer
  def PauseResumeSync(self, pause):
570 89ff748d Thomas Thrainer
    """Pauses or resumes the sync of a DRBD device.
571 89ff748d Thomas Thrainer

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

574 89ff748d Thomas Thrainer
    """
575 89ff748d Thomas Thrainer
    if self.minor is None:
576 89ff748d Thomas Thrainer
      logging.info("Not attached during PauseSync")
577 89ff748d Thomas Thrainer
      return False
578 89ff748d Thomas Thrainer
579 239364d0 Thomas Thrainer
    children_result = super(DRBD8Dev, self).PauseResumeSync(pause)
580 89ff748d Thomas Thrainer
581 89ff748d Thomas Thrainer
    if pause:
582 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenPauseSyncCmd(self.minor)
583 89ff748d Thomas Thrainer
    else:
584 09a78e1c Thomas Thrainer
      cmd = self._cmd_gen.GenResumeSyncCmd(self.minor)
585 89ff748d Thomas Thrainer
586 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
587 89ff748d Thomas Thrainer
    if result.failed:
588 89ff748d Thomas Thrainer
      logging.error("Can't %s: %s - %s", cmd,
589 89ff748d Thomas Thrainer
                    result.fail_reason, result.output)
590 89ff748d Thomas Thrainer
    return not result.failed and children_result
591 89ff748d Thomas Thrainer
592 89ff748d Thomas Thrainer
  def GetProcStatus(self):
593 47e0abee Thomas Thrainer
    """Return the current status data from /proc/drbd for this device.
594 47e0abee Thomas Thrainer

595 47e0abee Thomas Thrainer
    @rtype: DRBD8Status
596 89ff748d Thomas Thrainer

597 89ff748d Thomas Thrainer
    """
598 89ff748d Thomas Thrainer
    if self.minor is None:
599 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: GetStats() called while not attached",
600 89ff748d Thomas Thrainer
                      self._aminor)
601 47e0abee Thomas Thrainer
    info = DRBD8.GetProcInfo()
602 47e0abee Thomas Thrainer
    if not info.HasMinorStatus(self.minor):
603 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't find myself in /proc", self.minor)
604 47e0abee Thomas Thrainer
    return info.GetMinorStatus(self.minor)
605 89ff748d Thomas Thrainer
606 89ff748d Thomas Thrainer
  def GetSyncStatus(self):
607 89ff748d Thomas Thrainer
    """Returns the sync status of the device.
608 89ff748d Thomas Thrainer

609 89ff748d Thomas Thrainer
    If sync_percent is None, it means all is ok
610 89ff748d Thomas Thrainer
    If estimated_time is None, it means we can't estimate
611 89ff748d Thomas Thrainer
    the time needed, otherwise it's the time left in seconds.
612 89ff748d Thomas Thrainer

613 89ff748d Thomas Thrainer
    We set the is_degraded parameter to True on two conditions:
614 89ff748d Thomas Thrainer
    network not connected or local disk missing.
615 89ff748d Thomas Thrainer

616 89ff748d Thomas Thrainer
    We compute the ldisk parameter based on whether we have a local
617 89ff748d Thomas Thrainer
    disk or not.
618 89ff748d Thomas Thrainer

619 89ff748d Thomas Thrainer
    @rtype: objects.BlockDevStatus
620 89ff748d Thomas Thrainer

621 89ff748d Thomas Thrainer
    """
622 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
623 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in GetSyncStatus", self._aminor)
624 89ff748d Thomas Thrainer
625 89ff748d Thomas Thrainer
    stats = self.GetProcStatus()
626 89ff748d Thomas Thrainer
    is_degraded = not stats.is_connected or not stats.is_disk_uptodate
627 89ff748d Thomas Thrainer
628 89ff748d Thomas Thrainer
    if stats.is_disk_uptodate:
629 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_OKAY
630 89ff748d Thomas Thrainer
    elif stats.is_diskless:
631 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_FAULTY
632 89ff748d Thomas Thrainer
    else:
633 89ff748d Thomas Thrainer
      ldisk_status = constants.LDS_UNKNOWN
634 89ff748d Thomas Thrainer
635 89ff748d Thomas Thrainer
    return objects.BlockDevStatus(dev_path=self.dev_path,
636 89ff748d Thomas Thrainer
                                  major=self.major,
637 89ff748d Thomas Thrainer
                                  minor=self.minor,
638 89ff748d Thomas Thrainer
                                  sync_percent=stats.sync_percent,
639 89ff748d Thomas Thrainer
                                  estimated_time=stats.est_time,
640 89ff748d Thomas Thrainer
                                  is_degraded=is_degraded,
641 89ff748d Thomas Thrainer
                                  ldisk_status=ldisk_status)
642 89ff748d Thomas Thrainer
643 89ff748d Thomas Thrainer
  def Open(self, force=False):
644 89ff748d Thomas Thrainer
    """Make the local state primary.
645 89ff748d Thomas Thrainer

646 47e0abee Thomas Thrainer
    If the 'force' parameter is given, DRBD is instructed to switch the device
647 47e0abee Thomas Thrainer
    into primary mode. Since this is a potentially dangerous operation, the
648 47e0abee Thomas Thrainer
    force flag should be only given after creation, when it actually is
649 47e0abee Thomas Thrainer
    mandatory.
650 89ff748d Thomas Thrainer

651 89ff748d Thomas Thrainer
    """
652 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
653 89ff748d Thomas Thrainer
      logging.error("DRBD cannot attach to a device during open")
654 89ff748d Thomas Thrainer
      return False
655 09a78e1c Thomas Thrainer
656 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenPrimaryCmd(self.minor, force)
657 09a78e1c Thomas Thrainer
658 89ff748d Thomas Thrainer
    result = utils.RunCmd(cmd)
659 89ff748d Thomas Thrainer
    if result.failed:
660 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't make drbd device primary: %s", self.minor,
661 89ff748d Thomas Thrainer
                      result.output)
662 89ff748d Thomas Thrainer
663 89ff748d Thomas Thrainer
  def Close(self):
664 89ff748d Thomas Thrainer
    """Make the local state secondary.
665 89ff748d Thomas Thrainer

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

668 89ff748d Thomas Thrainer
    """
669 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
670 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't Attach() in Close()", self._aminor)
671 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenSecondaryCmd(self.minor)
672 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
673 89ff748d Thomas Thrainer
    if result.failed:
674 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't switch drbd device to secondary: %s",
675 89ff748d Thomas Thrainer
                      self.minor, result.output)
676 89ff748d Thomas Thrainer
677 89ff748d Thomas Thrainer
  def DisconnectNet(self):
678 89ff748d Thomas Thrainer
    """Removes network configuration.
679 89ff748d Thomas Thrainer

680 89ff748d Thomas Thrainer
    This method shutdowns the network side of the device.
681 89ff748d Thomas Thrainer

682 89ff748d Thomas Thrainer
    The method will wait up to a hardcoded timeout for the device to
683 89ff748d Thomas Thrainer
    go into standalone after the 'disconnect' command before
684 89ff748d Thomas Thrainer
    re-configuring it, as sometimes it takes a while for the
685 89ff748d Thomas Thrainer
    disconnect to actually propagate and thus we might issue a 'net'
686 89ff748d Thomas Thrainer
    command while the device is still connected. If the device will
687 89ff748d Thomas Thrainer
    still be attached to the network and we time out, we raise an
688 89ff748d Thomas Thrainer
    exception.
689 89ff748d Thomas Thrainer

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

742 89ff748d Thomas Thrainer
    This method connects the network side of the device with a
743 89ff748d Thomas Thrainer
    specified multi-master flag. The device needs to be 'Standalone'
744 89ff748d Thomas Thrainer
    but have valid network configuration data.
745 89ff748d Thomas Thrainer

746 47e0abee Thomas Thrainer
    @type multimaster: boolean
747 47e0abee Thomas Thrainer
    @param multimaster: init the network in dual-primary mode
748 89ff748d Thomas Thrainer

749 89ff748d Thomas Thrainer
    """
750 89ff748d Thomas Thrainer
    if self.minor is None:
751 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device not attached in AttachNet", self._aminor)
752 89ff748d Thomas Thrainer
753 89ff748d Thomas Thrainer
    if None in (self._lhost, self._lport, self._rhost, self._rport):
754 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: missing network info in AttachNet()", self.minor)
755 89ff748d Thomas Thrainer
756 89ff748d Thomas Thrainer
    status = self.GetProcStatus()
757 89ff748d Thomas Thrainer
758 89ff748d Thomas Thrainer
    if not status.is_standalone:
759 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: device is not standalone in AttachNet",
760 89ff748d Thomas Thrainer
                      self.minor)
761 89ff748d Thomas Thrainer
762 89ff748d Thomas Thrainer
    self._AssembleNet(self.minor,
763 89ff748d Thomas Thrainer
                      (self._lhost, self._lport, self._rhost, self._rport),
764 65fc2388 Thomas Thrainer
                      dual_pri=multimaster, hmac=constants.DRBD_HMAC_ALG,
765 65fc2388 Thomas Thrainer
                      secret=self._secret)
766 89ff748d Thomas Thrainer
767 89ff748d Thomas Thrainer
  def Attach(self):
768 89ff748d Thomas Thrainer
    """Check if our minor is configured.
769 89ff748d Thomas Thrainer

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

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

777 89ff748d Thomas Thrainer
    """
778 47e0abee Thomas Thrainer
    used_devs = DRBD8.GetUsedDevs()
779 89ff748d Thomas Thrainer
    if self._aminor in used_devs:
780 89ff748d Thomas Thrainer
      minor = self._aminor
781 89ff748d Thomas Thrainer
    else:
782 89ff748d Thomas Thrainer
      minor = None
783 89ff748d Thomas Thrainer
784 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
785 89ff748d Thomas Thrainer
    return minor is not None
786 89ff748d Thomas Thrainer
787 89ff748d Thomas Thrainer
  def Assemble(self):
788 89ff748d Thomas Thrainer
    """Assemble the drbd.
789 89ff748d Thomas Thrainer

790 89ff748d Thomas Thrainer
    Method:
791 89ff748d Thomas Thrainer
      - if we have a configured device, we try to ensure that it matches
792 89ff748d Thomas Thrainer
        our config
793 89ff748d Thomas Thrainer
      - if not, we create it from zero
794 89ff748d Thomas Thrainer
      - anyway, set the device parameters
795 89ff748d Thomas Thrainer

796 89ff748d Thomas Thrainer
    """
797 239364d0 Thomas Thrainer
    super(DRBD8Dev, self).Assemble()
798 89ff748d Thomas Thrainer
799 89ff748d Thomas Thrainer
    self.Attach()
800 89ff748d Thomas Thrainer
    if self.minor is None:
801 89ff748d Thomas Thrainer
      # local device completely unconfigured
802 89ff748d Thomas Thrainer
      self._FastAssemble()
803 89ff748d Thomas Thrainer
    else:
804 89ff748d Thomas Thrainer
      # we have to recheck the local and network status and try to fix
805 89ff748d Thomas Thrainer
      # the device
806 89ff748d Thomas Thrainer
      self._SlowAssemble()
807 89ff748d Thomas Thrainer
808 89ff748d Thomas Thrainer
    sync_errors = self.SetSyncParams(self.params)
809 89ff748d Thomas Thrainer
    if sync_errors:
810 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't set the synchronization parameters: %s" %
811 89ff748d Thomas Thrainer
                      (self.minor, utils.CommaJoin(sync_errors)))
812 89ff748d Thomas Thrainer
813 89ff748d Thomas Thrainer
  def _SlowAssemble(self):
814 89ff748d Thomas Thrainer
    """Assembles the DRBD device from a (partially) configured device.
815 89ff748d Thomas Thrainer

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

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

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

893 89ff748d Thomas Thrainer
    """
894 89ff748d Thomas Thrainer
    minor = self._aminor
895 89ff748d Thomas Thrainer
    if self._children and self._children[0] and self._children[1]:
896 89ff748d Thomas Thrainer
      self._AssembleLocal(minor, self._children[0].dev_path,
897 89ff748d Thomas Thrainer
                          self._children[1].dev_path, self.size)
898 89ff748d Thomas Thrainer
    if self._lhost and self._lport and self._rhost and self._rport:
899 89ff748d Thomas Thrainer
      self._AssembleNet(minor,
900 89ff748d Thomas Thrainer
                        (self._lhost, self._lport, self._rhost, self._rport),
901 89ff748d Thomas Thrainer
                        hmac=constants.DRBD_HMAC_ALG, secret=self._secret)
902 89ff748d Thomas Thrainer
    self._SetFromMinor(minor)
903 89ff748d Thomas Thrainer
904 09a78e1c Thomas Thrainer
  def _ShutdownLocal(self, minor):
905 89ff748d Thomas Thrainer
    """Detach from the local device.
906 89ff748d Thomas Thrainer

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

910 47e0abee Thomas Thrainer
    @type minor: int
911 47e0abee Thomas Thrainer
    @param minor: the device to detach from the local device
912 47e0abee Thomas Thrainer

913 89ff748d Thomas Thrainer
    """
914 09a78e1c Thomas Thrainer
    cmd = self._cmd_gen.GenDetachCmd(minor)
915 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
916 89ff748d Thomas Thrainer
    if result.failed:
917 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't detach local disk: %s",
918 89ff748d Thomas Thrainer
                      minor, result.output)
919 89ff748d Thomas Thrainer
920 09a78e1c Thomas Thrainer
  def _ShutdownNet(self, minor):
921 89ff748d Thomas Thrainer
    """Disconnect from the remote peer.
922 89ff748d Thomas Thrainer

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

925 47e0abee Thomas Thrainer
    @type minor: boolean
926 47e0abee Thomas Thrainer
    @param minor: the device to disconnect from the remote peer
927 47e0abee Thomas Thrainer

928 89ff748d Thomas Thrainer
    """
929 daec28a7 Thomas Thrainer
    family = self._GetNetFamily(minor, self._lhost, self._rhost)
930 daec28a7 Thomas Thrainer
    cmd = self._cmd_gen.GenDisconnectCmd(minor, family,
931 daec28a7 Thomas Thrainer
                                         self._lhost, self._lport,
932 daec28a7 Thomas Thrainer
                                         self._rhost, self._rport)
933 09a78e1c Thomas Thrainer
    result = utils.RunCmd(cmd)
934 89ff748d Thomas Thrainer
    if result.failed:
935 89ff748d Thomas Thrainer
      base.ThrowError("drbd%d: can't shutdown network: %s",
936 89ff748d Thomas Thrainer
                      minor, result.output)
937 89ff748d Thomas Thrainer
938 89ff748d Thomas Thrainer
  def Shutdown(self):
939 89ff748d Thomas Thrainer
    """Shutdown the DRBD device.
940 89ff748d Thomas Thrainer

941 89ff748d Thomas Thrainer
    """
942 89ff748d Thomas Thrainer
    if self.minor is None and not self.Attach():
943 89ff748d Thomas Thrainer
      logging.info("drbd%d: not attached during Shutdown()", self._aminor)
944 89ff748d Thomas Thrainer
      return
945 d0d7d7cf Thomas Thrainer
946 d0d7d7cf Thomas Thrainer
    try:
947 d0d7d7cf Thomas Thrainer
      DRBD8.ShutdownAll(self.minor)
948 d0d7d7cf Thomas Thrainer
    finally:
949 d0d7d7cf Thomas Thrainer
      self.minor = None
950 d0d7d7cf Thomas Thrainer
      self.dev_path = None
951 89ff748d Thomas Thrainer
952 89ff748d Thomas Thrainer
  def Remove(self):
953 89ff748d Thomas Thrainer
    """Stub remove for DRBD devices.
954 89ff748d Thomas Thrainer

955 89ff748d Thomas Thrainer
    """
956 89ff748d Thomas Thrainer
    self.Shutdown()
957 89ff748d Thomas Thrainer
958 fd300bc7 Thomas Thrainer
  def Rename(self, new_id):
959 fd300bc7 Thomas Thrainer
    """Rename a device.
960 fd300bc7 Thomas Thrainer

961 fd300bc7 Thomas Thrainer
    This is not supported for drbd devices.
962 fd300bc7 Thomas Thrainer

963 fd300bc7 Thomas Thrainer
    """
964 fd300bc7 Thomas Thrainer
    raise errors.ProgrammerError("Can't rename a drbd device")
965 fd300bc7 Thomas Thrainer
966 be9150ea Bernardo Dal Seno
  def Grow(self, amount, dryrun, backingstore, excl_stor):
967 47e0abee Thomas Thrainer
    """Resize the DRBD device and its backing storage.
968 47e0abee Thomas Thrainer

969 47e0abee Thomas Thrainer
    See L{BlockDev.Grow} for parameter description.
970 47e0abee Thomas Thrainer

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

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

992 47e0abee Thomas Thrainer
    @type minor: int
993 47e0abee Thomas Thrainer
    @param minor: the DRBD minor whose (future) meta device should be
994 47e0abee Thomas Thrainer
      initialized
995 47e0abee Thomas Thrainer
    @type dev_path: string
996 47e0abee Thomas Thrainer
    @param dev_path: path to the meta device to initialize
997 47e0abee Thomas Thrainer

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

1023 89ff748d Thomas Thrainer
    Since DRBD devices are not created per se, just assembled, this
1024 89ff748d Thomas Thrainer
    function only initializes the metadata.
1025 89ff748d Thomas Thrainer

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

1060 89ff748d Thomas Thrainer
  This tries to read the first 128k of the device.
1061 89ff748d Thomas Thrainer

1062 47e0abee Thomas Thrainer
  @type path: string
1063 47e0abee Thomas Thrainer

1064 89ff748d Thomas Thrainer
  """
1065 89ff748d Thomas Thrainer
  try:
1066 89ff748d Thomas Thrainer
    utils.ReadFile(path, size=_DEVICE_READ_SIZE)
1067 89ff748d Thomas Thrainer
    return True
1068 89ff748d Thomas Thrainer
  except EnvironmentError:
1069 89ff748d Thomas Thrainer
    logging.warning("Can't read from device %s", path, exc_info=True)
1070 89ff748d Thomas Thrainer
    return False